PublishedMap.vue
This is where the users can see and interact with the maps published by them and other Mapmama users.
import
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
export default// Exporting the Vue component
export default {}Inside this is where we define data, methods, computed properties, and lifecycle hooks.
components
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// Data properties of the component
data() {
return {
tableTitle: "",
domainBackend: "",
domainFrontend: "",
map: null,
allData: [],
currentData: [],
};
}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
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 frontend domain from the Vuex store
this.domainFrontend = this.$store.state.domainFrontend;
// Setting backend domain from the Vuex store
this.domainBackend = this.$store.state.domainBackend;
// Initialize the map with published data by fetching it from the backend.
this.getPublishedMap();
}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:
It scrolls the window to the top so the user sees the content at the beginning of the page.
It gets the backend and frontend domain from what is defined in the Vuex.
It will get the JSON representation of the map data array of all the published maps from the backend.
methods
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
getPublishedMap
getPublishedMap// Method to fetch published map data from the backend and initialize the map with markers
getPublishedMap() {
// Update map buttons (UI elements related to the map)
this.updateMapButtons();
// Make an asynchronous POST request to the backend to get published map data
axios
.post(this.domainBackend + "/api/getPublishedMap")
.then((response) => {
// Check if the backend response indicates success (SUCCESS == 1)
if (response.data.SUCCESS == 1) {
// Store the retrieved map data in the component's state
this.allData = response.data.DATA;
this.currentData = this.allData[0];
// Check if there is any map data available
if (this.allData.length == 0) {
// If no data is available, initialize the map with default settings
this.initMap();
this.updateMapButtons();
this.tableTitle = "Table";
} else {
// If map data is available, add markers to the map, update the table, and set the map title
this.addMarkers(this.allData[0]);
this.updateTable(this.allData[0]);
this.updateMapButtons();
this.tableTitle = this.allData[0].TITLE;
}
}
});
}To explain what this getPublishedMap method does:
First, it will update the map entries in the selection section.
Then, it will get the JSON representation of the map data array of all the published maps from the backend.
It initializes the map data array and the currently selected map data.
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.
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.
openShareToSocial
openShareToSocial// Method to handle the process of sharing a map to social media platforms
openShareToSocial() {
// Prepare the request payload containing the map title and data in JSON format
const request = {
jsonData: JSON.stringify({
TITLE: this.currentData.TITLE,
DATA: this.currentData.DATA,
}),
};
// Make an asynchronous POST request to the backend to store the shared map
axios
.post(this.domainBackend + "/api/storeSharedMap", request)
.then((response) => {
// Check if the backend response indicates success (SUCCESS == 1)
if (response.data.SUCCESS == 1) {
// Retrieve the generated URL for the shared map and update the UI
const urlLink = document.getElementById("urlLink");
urlLink.value =
this.domainFrontend + "/shared-map/" + response.data.FILENAME;
// Display the container for sharing on social media
const containerShareToSocial = document.getElementById(
"containerShareToSocial"
);
containerShareToSocial.style.display = "block";
}
});
}To explain what this openShareToSocial method does:
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.
After that, a pop-up box will be shown where the user can copy the URL of the shared map.
closeShareToSocial
closeShareToSocial// Method to close the social media sharing interface
closeShareToSocial() {
// Retrieve the container element for sharing on social media
const containerShareToSocial = document.getElementById(
"containerShareToSocial"
);
// Hide the container by setting its display property to "none"
containerShareToSocial.style.display = "none";
}To explain what this closeShareToSocial method does:
It will the close the pop-up box.
convertJsonToGoogleSheet
convertJsonToGoogleSheet// Method to convert the current map data into a Google Sheet format
convertJsonToGoogleSheet() {
// Capture relevant data for the Google Sheet conversion
const domainBackend = this.domainBackend; // Backend domain URL
const title = this.currentData.TITLE; // Title of the current map
const data = JSON.parse(JSON.stringify(this.currentData.DATA)); // Deep copy of map data
data.forEach((item) => {
// Remove unnecessary properties for Google Sheet
delete item.LATITUDE;
delete item.LONGITUDE;
});
const jsonData = JSON.stringify(data); // Convert cleaned data back to JSON
// Define properties for the Google Sheet authentication window
const popupProperties = "width=500,height=800,scrollbars=yes";
let windowPopUp;
// Open a new window for Google Sheet authentication
windowPopUp = window.open("", "Google", popupProperties);
// Initiate authentication by making a GET request to the backend
axios
.get(
this.domainBackend + "/api/authenticateGoogleAccountForGoogleSheet"
)
.then((response) => {
// Check if authentication was successful
if (response.data.SUCCESS == 1) {
const url = response.data.AUTH_URL;
// Redirect the authentication window to the Google authentication URL
windowPopUp.location.href = url;
// Monitor changes in the authentication window's URL
monitorUrlChanges();
}
});
// Function to monitor changes in the authentication window's URL
function monitorUrlChanges() {
const interval = setInterval(() => {
if (windowPopUp && !windowPopUp.closed) {
const urlPopUp = windowPopUp.location.href;
// Check if the URL contains the authentication code
if (urlPopUp.includes("?code=")) {
const params = new URLSearchParams(urlPopUp.split("?")[1]);
const code = params.get("code");
// Send the JSON data and authentication code to the backend
sendJson(code);
// Stop monitoring URL changes
clearInterval(interval);
}
} else {
// Stop monitoring URL changes if the window is closed
clearInterval(interval);
}
}, 1000);
}
// Function to send JSON data and authentication code to the backend for Google Sheet conversion
function sendJson(code) {
const request = {
title: title,
jsonData: jsonData,
code: code,
};
// Make a POST request to the backend for Google Sheet conversion
axios
.post(domainBackend + "/api/convertJsonToGoogleSheet", request)
.then((response) => {
// Close the authentication window and open the generated Google Sheet link in a new window
windowPopUp.close();
window.open(response.data.LINK, "_blank").focus();
});
}
}To explain what this convertJsonToGoogleSheet method does:
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.
The authentication URL will be opened in a pop-up window.
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.
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.
After converting the JSON data to Google Sheets, the backend will respond with a link to the Google Sheets file.
The link is automatically opened in a new tab.
downloadJson
downloadJson// Method to download the current map data as a JSON file
downloadJson() {
// Create a deep copy of the map data and remove latitude and longitude properties
const data = JSON.parse(JSON.stringify(this.currentData.DATA));
data.forEach((item) => {
delete item.LATITUDE;
delete item.LONGITUDE;
});
// Convert the cleaned data back to JSON format with proper indentation
const jsonData = JSON.stringify(data, null, 2);
// Create a Blob (Binary Large Object) from the JSON data with MIME type "application/json"
const blob = new Blob([jsonData], { type: "application/json" });
// Create a URL for the Blob
const url = window.URL.createObjectURL(blob);
// Create an <a> element to represent the download link
const a = document.createElement("a");
a.href = url;
// Set the download attribute to specify the filename for the downloaded file
a.download = this.currentData.TITLE + ".json";
// Append the <a> element to the document body
document.body.appendChild(a);
// Simulate a click on the <a> element to trigger the download
a.click();
// Revoke the object URL to free up resources
window.URL.revokeObjectURL(url);
// Remove the <a> element from the document body
document.body.removeChild(a);
}To explain what this downloadJson method does:
It will get the currently selected map data and convert it to a BLOB object that encapsulates the JSON data.
Then, it programmatically initiates the download of the file.
convertJsonToCsv
convertJsonToCsv// Method to convert the current map data into CSV format and download it
convertJsonToCsv() {
// Create a deep copy of the map data and remove latitude and longitude properties
const data = JSON.parse(JSON.stringify(this.currentData.DATA));
data.forEach((item) => {
delete item.LATITUDE;
delete item.LONGITUDE;
});
// Prepare the request object with JSON data
const request = {
jsonData: JSON.stringify(data),
};
// Make a POST request to the server to convert JSON to CSV
axios
.post(this.domainBackend + "/api/convertJsonToCsv", request)
.then((response) => {
// Check if the conversion was successful
if (response.data.SUCCESS == 1) {
// Create a Blob (Binary Large Object) from the CSV data with MIME type "text/csv"
const blob = new Blob([response.data.DATA], { type: "text/csv" });
// Create a URL for the Blob
const url = window.URL.createObjectURL(blob);
// Create an <a> element to represent the download link
const a = document.createElement("a");
a.href = url;
// Set the download attribute to specify the filename for the downloaded CSV file
a.download = this.currentData.TITLE + ".csv";
// Append the <a> element to the document body
document.body.appendChild(a);
// Simulate a click on the <a> element to trigger the download
a.click();
// Revoke the object URL to free up resources
window.URL.revokeObjectURL(url);
// Remove the <a> element from the document body
document.body.removeChild(a);
}
});
}To explain what this convertJsonToCsv method does:
It sends the JSON representation of the currently selected map data to the backend.
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.
Then, it programmatically initiates the download of the file.
openExportMap
openExportMap// Method to display the export map interface
openExportMap() {
// Get the HTML element with the id "containerExportMap"
const containerExportMap = document.getElementById("containerExportMap");
// Set the display style of the container to "block" to make it visible
containerExportMap.style.display = "block";
}To explain what this openExportMap method does:
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
closeExportMap// Method to close the export map interface
closeExportMap() {
// Get the HTML element with the id "containerExportMap"
const containerExportMap = document.getElementById("containerExportMap");
// Set the display style of the container to "none" to hide it
containerExportMap.style.display = "none";
}To explain what this closeExportMap method does:
It will the close the pop-up box.
deleteMap
deleteMap// Method to delete a specific map from the list of published maps
deleteMap(index) {
// Loop through allData array to find the map with the specified index
for (let i = 0; i < this.allData.length; i++) {
// Check if the current iteration matches the specified index
if (i == index) {
// Delete the map by calling the deletePublishedMap method with the map's ID
this.deletePublishedMap(this.allData[i].ID);
// Remove the map from the allData array
this.allData.splice(i, 1);
// Set the currentData to the first map in the updated list
this.currentData = this.allData[0];
// Break out of the loop once the map is deleted
break;
}
}
// Update the map buttons based on the modified list of maps
this.updateMapButtons();
// Check if there are remaining maps
if (this.allData.length == 0) {
// If no maps are left, initialize the map, update the table, and set the table title
this.initMap();
this.updateTable([]);
this.tableTitle = "Table";
} else {
// If there are remaining maps, add markers, update the table, and set the table title
this.addMarkers(this.currentData);
this.updateTable(this.currentData);
this.tableTitle = this.currentData.TITLE;
}
}To explain what this deleteMap method does:
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.
Then, the map data array is looped until the index matches.
Once the index matches, the map data will be removed from the map data array.
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.
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.
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.
If the user is not logged in, then the map data array is saved to the cookie.
deletePublishedMap
deletePublishedMap// Method to remove a published map from the backend
deletePublishedMap(id) {
// Create a request object with the fileName property set to the provided map ID
const request = {
fileName: id,
};
// Send a POST request to the backend API to delete the published map
axios
.post(this.domainBackend + "/api/deletePublishedMap", request)
.then((response) => {
// Check if the deletion on the backend was successful
if (response.data.SUCCESS == 1) {
// If successful, do nothing (no further action required)
}
});
}To explain what this deletePublishedMap method does:
It will send the ID of the published map which is also the file name of it to the backend.
The backend will do the deletion of the particular published map file.
updatePublishedMap
updatePublishedMap// Method to update a published map with new data on the backend
updatePublishedMap() {
// Variable to store a duplicate of the map data for the currently logged-in user
var duplicate_allData = null;
// Iterate through all published maps
for (let i = 0; i < this.allData.length; i++) {
// Check if the current map belongs to the logged-in user
if (this.allData[i].USER_ID == this.$cookie.getCookie("L_userId")) {
// Create a deep copy (duplicate) of the user's map data
duplicate_allData = JSON.parse(JSON.stringify(this.allData[i]));
}
}
// Create a request object with the duplicated map data converted to a JSON string
const request = {
jsonData: JSON.stringify(duplicate_allData),
};
// Send a POST request to the backend API to update the published map
axios
.post(this.domainBackend + "/api/storePublishedMap", request)
.then((response) => {
// Check if the update on the backend was successful
if (response.data.SUCCESS == 1) {
// If successful, do nothing (no further action required)
}
});
}To explain what this updatePublishedMap method does:
It sends the JSON representation of the map data array to the backend.
The backend will write the JSON onto the particular published map file of the user.
updateMapButtons
updateMapButtons// Method to dynamically update the map buttons based on available data
updateMapButtons() {
// Get the container element for map buttons from the DOM
const mapButtonsContainer = document.getElementById(
"map-buttons-container"
);
// Clear the existing content of the container
mapButtonsContainer.innerHTML = "";
// Iterate through all available map data
for (let i = 0; i < this.allData.length; i++) {
// Create the first container div for each map
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 strong tag to display the map title
const strongTag = document.createElement("strong");
strongTag.innerText = this.allData[i].TITLE;
// Create an anchor tag inside the second div for map details
const aTagInsideSecondDiv = document.createElement("a");
aTagInsideSecondDiv.classList.add("nav-item", "nav-link", "text-white");
aTagInsideSecondDiv.innerHTML =
"<strong style='color:white'>" +
this.allData[i].TITLE +
"</strong>" +
" by " +
this.allData[i].USERNAME;
// Add a click event listener to the anchor tag to update the UI when a map is selected
aTagInsideSecondDiv.addEventListener("click", () => {
this.tableTitle = this.allData[i].TITLE;
this.addMarkers(this.allData[i]);
this.updateTable(this.allData[i]);
this.currentData = this.allData[i];
});
// Set cursor style to pointer for better user interaction
aTagInsideSecondDiv.style.cursor = "pointer";
// Create the second div and append the anchor tag to it
const secondDiv = document.createElement("div");
secondDiv.style.width = "70%";
secondDiv.appendChild(aTagInsideSecondDiv);
// Append the strong tag to the first div
firstDiv.appendChild(secondDiv);
// Create the third div for map deletion option
const thirdDiv = document.createElement("div");
// Create an icon element for the delete option
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 a click event listener to the icon to delete the corresponding map
iconElement.addEventListener("click", () => {
this.deleteMap(i);
});
// Check if the user is the owner of the map, and only then show the delete option
if (
this.$cookie.isCookieAvailable("L_userId") == true &&
this.$cookie.getCookie("L_userId") == this.allData[i].USER_ID
) {
thirdDiv.appendChild(iconElement);
} else {
// Placeholder for non-owner user (can be customized based on requirements)
const iconElement = document.createElement("i");
thirdDiv.appendChild(iconElement);
}
// Append the third div to the first div
firstDiv.appendChild(thirdDiv);
// Append the first div to the map buttons container in the DOM
mapButtonsContainer.appendChild(firstDiv);
}
}To explain what this updateMapButtons method does:
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.
initMap
initMap// Method to initialize the Google Map with default settings
initMap() {
// Create a new instance of the Google Map and set its properties
this.map = new google.maps.Map(document.getElementById("map"), {
// Set the initial center of the map to (0, 0)
center: { lat: 0, lng: 0 },
// Set the initial zoom level of the map to 2
zoom: 2,
// Custom property to store markers associated with the map (initially an empty array)
markers: [],
});
}To explain what this initMap method does:
It initializes a Google Map on the page.
addMarkers
addMarkers// Method to add markers to the map based on the provided data
addMarkers(data) {
// Check if there is data to display
if (data.DATA.length != 0) {
// Create a deep copy of the data to avoid unintentional modification
const duplicate_data = JSON.parse(JSON.stringify(data.DATA));
// Remove the "WKT" property from each item in the copied data
duplicate_data.forEach((item) => {
if (item.hasOwnProperty("WKT")) {
delete item.WKT;
}
});
// Create a proxy for the duplicated data to handle getter and setter functions
const proxy_data = new Proxy(duplicate_data, {
get: function (target, prop) {
return target[prop];
},
set: function (target, prop, value) {
target[prop] = value;
return true;
},
});
// Reset the map and initialize a new one
this.map = null;
this.initMap();
// Create an array to store markers and initialize an info window
const markers = [];
let currentInfoWindow = null;
// Loop through each place in the data to create markers
proxy_data.forEach((place) => {
// Create a new marker for each place
const marker = new google.maps.Marker({
position: {
lat: parseFloat(place.LATITUDE),
lng: parseFloat(place.LONGITUDE),
},
map: this.map,
title: place.NAME,
});
// Add a click event listener to each marker
marker.addListener("click", () => {
// Close the current info window if it exists
if (currentInfoWindow) {
currentInfoWindow.close();
}
// Create a content div for the info window
const mainDiv = document.createElement("div");
// Create divs for title, coordinates, and icons
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");
// Create share, locate, edit, and bookmark buttons
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>';
if (
this.$cookie.isCookieAvailable("L_userId") == true &&
this.$cookie.getCookie("L_userId") == data.USER_ID
) {
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>';
iconsHolderDiv.appendChild(editButtonDiv);
}
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(bookmarkButtonDiv);
// Append created elements to the main div
mainDiv.appendChild(titleHolderDiv);
mainDiv.appendChild(coordinateHolderDiv);
mainDiv.appendChild(iconsHolderDiv);
// Create an info window and open it with the main div content
const infowindow = new google.maps.InfoWindow({
content: mainDiv,
});
infowindow.open(this.ETM_map, marker);
currentInfoWindow = infowindow;
});
const map = this.map;
// Store the marker in the markers array
markers.push(marker);
this.map = map;
});
// Set the map center and zoom level based on the first place's coordinates
const map = this.map;
map.setCenter({
lat: parseFloat(proxy_data[0].LATITUDE),
lng: parseFloat(proxy_data[0].LONGITUDE),
});
map.setZoom(10);
// Create a marker cluster for better visualization
const markerCluster = new MarkerClusterer({
map: map,
});
// Add each marker to the cluster
markers.forEach((marker) => {
markerCluster.addMarker(marker);
});
// Store the markers array in the map property
map.markers = markers;
this.map = map;
} else {
// If there is no data, reset the map
this.map = null;
this.initMap();
}
}To explain what this addMarkers method does:
if the map data is not empty, it proceeds with the following steps; otherwise, it initializes the map and sets it to null.
It initializes a Google Map and creates an empty array to hold markers.
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.
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.
The markers are clustered together for better visualization when there are multiple markers in close proximity.
updateTable
updateTable// Method to update the data table based on the current map data
updateTable(data) {
// Create a deep copy of the data to avoid unintentional modification
const duplicate_data = JSON.parse(JSON.stringify(data.DATA));
// Remove the "WKT" property from each item in the copied data
duplicate_data.forEach((item) => {
if (item.hasOwnProperty("WKT")) {
delete item.WKT;
}
});
// Create a proxy for the duplicated data to handle getter and setter functions
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 references to the table head row and table body
const tableHeadRow = document.getElementById("table-tr");
const tableBody = document.getElementById("mtsTbody");
// Clear the existing content of the table
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 the data
const allProperties = new Set();
// Iterate through each item to collect all unique property names
proxy_data.forEach((item) => {
Object.keys(item).forEach((propertyName) => {
allProperties.add(propertyName);
});
});
// Create table header cells based on unique property names
allProperties.forEach((propertyName) => {
const th = document.createElement("th");
th.textContent = propertyName;
tableHeadRow.appendChild(th);
});
// Create "EDIT" and "OTHER ACTION" columns in the table header
let th = document.createElement("th");
if (
this.$cookie.isCookieAvailable("L_userId") == true &&
this.$cookie.getCookie("L_userId") == data.USER_ID
) {
th.textContent = "EDIT";
tableHeadRow.appendChild(th);
}
th = document.createElement("th");
th.textContent = "OTHER ACTION";
tableHeadRow.appendChild(th);
// Iterate through each item to create table rows
proxy_data.forEach((item) => {
const row = document.createElement("tr");
// Create table cells for each property
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 {
// Handle null or undefined values if needed
// cell.style.backgroundColor = "#adadad";
}
row.appendChild(cell);
});
// If user has editing permissions, create an "EDIT" button cell
if (
this.$cookie.isCookieAvailable("L_userId") == true &&
this.$cookie.getCookie("L_userId") == data.USER_ID
) {
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 an "OTHER ACTION" cell with delete, locate, and share buttons
const otherCell = document.createElement("td");
// If user has deleting permissions, create a "DELETE" button
if (
this.$cookie.isCookieAvailable("L_userId") == true &&
this.$cookie.getCookie("L_userId") == data.USER_ID
) {
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);
}
// Create a "LOCATE" button with a link to Google Maps
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);
// Create a "SHARE" button with a callback to the shareRow method
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:
It creates a deep copy of the map data using JSON serialization and parsing. This is done to avoid modifying the original data.
It removes the "WKT" property from each object in map data if it exists.
It creates a Proxy object of the map data that allows getting and setting properties while ensuring they are always set successfully.
It selects the table's header row and body elements in the HTML document.
It clears the existing content of the table's header row and body.
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.
It iterates over the properties of the objects to create table header cells and appends them to the header row.
For each object in the Proxy object, it generates a new row in the table body, filling in cells with property values.
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
bookmarkRow// Method to bookmark a specific row/item in the map data
bookmarkRow(itemForBookmark) {
// Check if the user is logged in by verifying the presence of a user ID cookie
if (!this.$cookie.isCookieAvailable("L_userId")) {
// Display an alert if the user is not logged in
alert("LOGIN REQUIRED.");
} else {
// Prepare a request object with the user ID to check existing bookmarks
const request = {
userId: this.$cookie.getCookie("L_userId"),
};
// Send a request to the backend to retrieve the user's bookmarks
axios
.post(this.domainBackend + "/api/getBookmark", request)
.then((response) => {
// Check if the request was successful
if (response.data.SUCCESS == 1) {
// Extract bookmark data from the response
const data = response.data.DATA;
// Variable to track if the bookmark already exists for the provided item
var isExist = false;
// Iterate through existing bookmarks to check for a match
data.forEach((item) => {
if (
item.LATITUDE.toString() ===
itemForBookmark.LATITUDE.toString() &&
item.LONGITUDE.toString() ===
itemForBookmark.LONGITUDE.toString()
) {
// Set the flag to true if a matching bookmark is found
isExist = true;
return; // Exit the loop early since the match is already found
}
});
// Check if the bookmark already exists
if (isExist == true) {
// Display an alert if the bookmark already exists
alert("BOOKMARK ALREADY EXISTS.");
} else {
// Add the provided item to the list of bookmarks
data.push(itemForBookmark);
// Prepare a new request to update the bookmarks with the new data
const updateRequest = {
jsonData: JSON.stringify({
USER_ID: this.$cookie.getCookie("L_userId"),
DATA: data,
}),
};
// Send a request to the backend to store the updated bookmarks
axios
.post(
this.domainBackend + "/api/storeBookmark",
updateRequest
)
.then((response) => {
// Check if the update was successful
if (response.data.SUCCESS == 1) {
// Display an alert indicating that the bookmark was updated
alert("BOOKMARK UPDATED.");
}
});
}
}
});
}
}To explain what this bookmarkRow method does:
If the user is not logged in, it displays an alert, indicating that the user must be logged in to use this feature.
it sends the user's ID to the backend to retrieve the data in the user's bookmark file.
It then checks if the going-to-be-added bookmark item already exists in the user's bookmark data.
If the item already exists in the user's bookmark data, displays an alert, indicating that the bookmark already exists.
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
deleteRow// Method to delete a specific row/item from the current map data
deleteRow(item) {
// Iterate through the current data to find and remove the specified item
for (let i = 0; i < this.currentData.DATA.length; i++) {
if (this.currentData.DATA[i].ID == item.ID) {
// Remove the item from the current data array
this.currentData.DATA.splice(i, 1);
break; // Exit the loop after the item is removed
}
}
// Iterate through all data to update the current data in the main data set
for (let i = 0; i < this.allData.length; i++) {
if (this.allData[i].ID == this.currentData.ID) {
// Update the corresponding entry in the main data set with the modified current data
this.allData[i] = this.currentData;
break; // Exit the loop after the update
}
}
// Check if the current data is now empty
if (this.currentData.DATA.length == 0) {
// Reset the map and initialize it if the current data is empty
this.map = null;
this.initMap();
} else {
// Add markers to the map for the modified current data
this.addMarkers(this.currentData);
}
// Update the table with the modified current data
this.updateTable(this.currentData);
// Update map buttons based on the modified data set
this.updateMapButtons();
// Update the published map with the modified data
this.updatePublishedMap();
}To explain what this deleteRow method does:
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.
Once the matching item is found, it removes it from the currently selected map data array.
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.
If there are no more items left in the currently selected map data, it sets the map to null and initializes a new map.
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.
The map data array and currently selected map data are saved to the cookie.
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
shareRow// Method to handle the sharing of a specific row/item in the map data
shareRow(item) {
// Get the URL link for the location based on its latitude and longitude
const urlLink = document.getElementById("urlLink");
urlLink.value =
"https://www.google.com/maps/search/?api=1&query=" +
item["LATITUDE"] +
"%2C" +
item["LONGITUDE"];
// Display the container for sharing on social media
const containerShareToSocial = document.getElementById(
"containerShareToSocial"
);
containerShareToSocial.style.display = "block";
}To explain what this shareRow method does:
A pop-up box will be shown where the user can copy the Google Maps link of the map item.
editRow
editRow// Method to open and populate the map editing interface with an item's data
editRow(item) {
// Toggle the visibility of the edit map container
const editMap = document.querySelector(".editMap");
editMap.classList.toggle("show");
// Set the title of the edit map container to the item's name
document.getElementById("editMap_Title").innerHTML =
"<strong>" + item["NAME"] + "</strong>";
// Clear the existing content of the edit map table and label classes container
const editMap_table = document.querySelector(".editMap_table");
editMap_table.innerHTML = "";
const labelClassesContainerEdit = document.getElementById(
"labelClassesContainerEdit"
);
labelClassesContainerEdit.innerHTML = "";
// Create a deep copy of the item for modifications
const modifiedItem = JSON.parse(JSON.stringify(item));
const propertyList = Object.keys(modifiedItem);
// Iterate through each property of the item
for (let i = 0; i < propertyList.length; i++) {
const propertyName = propertyList[i];
const propertyValue = modifiedItem[propertyName];
// Create a form group div for each property
const divElement = document.createElement("div");
divElement.className = "form-group";
// Create a label 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);
// If the property is not an ID or coordinates, allow deletion
if (
propertyName == "ID" ||
propertyName == "NAME" ||
propertyName == "LATITUDE" ||
propertyName == "LONGITUDE"
) {
// Do nothing for these properties
} else {
// Create a delete button for other properties
var deleteButton = document.createElement("div");
deleteButton.setAttribute("class", "buttonFormElementDelete");
deleteButton.textContent = "DELETE";
deleteButton.addEventListener("click", () => {
divElement.remove();
});
divElement.appendChild(deleteButton);
}
// Create an input element for editing the property value
const inputElement = document.createElement("input");
inputElement.id = propertyName;
inputElement.type = "text";
inputElement.className = "form-control";
inputElement.value = propertyValue;
// Make ID and coordinates read-only
if (
propertyName == "ID" ||
propertyName == "LATITUDE" ||
propertyName == "LONGITUDE"
) {
inputElement.readOnly = true;
} else {
inputElement.readOnly = false;
}
divElement.appendChild(inputElement);
// Append the form group to the edit map table
editMap_table.appendChild(divElement);
}
// Add an event listener to the "Save Changes" button
const saveChanges = document.getElementById("saveChanges");
saveChanges.addEventListener("click", () => {
// Retrieve edited values from the form and update the item
var editMap_table = document.getElementById("editMap_table");
var labelList = editMap_table.querySelectorAll("label");
var item = {};
// Iterate through each label and update the item with the corresponding input value
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;
}
// Update the currentData.DATA array with the modified item
for (let i = 0; i < this.currentData.DATA.length; i++) {
if (this.currentData.DATA[i].ID == item.ID) {
this.currentData.DATA[i] = item;
}
}
// Update the allData array with the modified currentData
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.DATA.ID) {
this.allData[i] = this.currentData;
}
}
}
// Update the map, table, map buttons, and the published map
this.addMarkers(this.currentData);
this.updateTable(this.currentData);
this.updateMapButtons();
this.updatePublishedMap();
});
}To explain what this editRow method does:
It toggles the visibility of the pop-up box for editing the row.
It updates the title of the pop-up box to display the name of the map item being edited.
It retrieves the list of property names from map item and iterates through them.
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.
When the "Save Changes" button is clicked, it retrieves the updated values of the properties from the input fields.
It uses a Proxy object to allow getting and setting properties in the item.
It updates the corresponding map item in the currently selected map data with the edited map item.
It also updated the map data array with the modified currently selected data.
It add the markers in the map, populates the table, and updates the map entries in the selection section.
The map data array and currently selected map data are saved to the cookie.
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
addLabelClassToEdit// Method to add a new label class to the map editing interface
addLabelClassToEdit() {
// 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 map form
var labelClassesContainerEdit = document.getElementById(
"labelClassesContainerEdit"
);
// Create a container for the new label class
var labelClassContainer = document.createElement("div");
labelClassContainer.classList.add("form-group");
// Create a label element for displaying the label 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 removing the label class
var deleteButton = document.createElement("div");
deleteButton.setAttribute("class", "buttonFormElementDelete");
deleteButton.textContent = "DELETE";
deleteButton.addEventListener("click", () => {
// Remove the label class container when the delete button is clicked
labelClassContainer.remove();
});
// Create an input element for the value associated with the label class
var valueInput = document.createElement("input");
valueInput.setAttribute("type", "text");
valueInput.setAttribute("class", "form-control");
valueInput.setAttribute("id", labelTitle.toUpperCase());
valueInput.setAttribute("value", "");
// Append elements to the label class container
labelClassContainer.appendChild(titleLabel);
labelClassContainer.appendChild(deleteButton);
labelClassContainer.appendChild(valueInput);
// Append the label class container to the label classes container
labelClassesContainerEdit.appendChild(labelClassContainer);
}
}To explain what this addLabelClassToEdit method does:
It prompts the user for a label title, and it stores the user's.
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.
It creates a label element for the title.
It creates an input field for the label's value, setting its initial value to an empty string.
It creates a "DELETE" button for removing the label, which triggers the removal of the label when clicked.
closeEditMap
closeEditMap// Method to close the map editing interface
closeEditMap() {
// Select the edit map form using its class
const editMap = document.querySelector(".editMap");
// Remove the "show" class to hide the edit map form
editMap.classList.remove("show");
}To explain what this closeEditMap method does:
It will the close the pop-up box for editing the row.
Last updated