-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
174 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,178 +1,208 @@ | ||
let isProcessingCSV = false; | ||
let isProcessingKML = false; | ||
|
||
const fetchMeetings = (query, callback, isCSV, isKML) => { | ||
const script = document.createElement("script"); | ||
script.src = `${query}&callback=${callback.name}`; | ||
document.body.appendChild(script); | ||
isProcessingCSV = isCSV; | ||
isProcessingKML = isKML; | ||
}; | ||
|
||
const handleMeetingsData = (meetings) => { | ||
if (isProcessingCSV) { | ||
class MeetingDataProcessor { | ||
constructor() { | ||
this.isProcessingCSV = false; | ||
this.isProcessingKML = false; | ||
} | ||
|
||
// D ynamically load JSONP data | ||
fetchMeetings(query, isCSV, isKML) { | ||
return new Promise((resolve, reject) => { | ||
const script = document.createElement("script"); | ||
const callbackName = `jsonpCallback_${Date.now()}`; | ||
|
||
window[callbackName] = (data) => { | ||
document.body.removeChild(script); | ||
delete window[callbackName]; | ||
this.handleMeetingsData(data, isCSV, isKML); | ||
resolve(data); | ||
}; | ||
|
||
script.src = `${query}&callback=${callbackName}`; | ||
script.onerror = () => reject(new Error("Script loading failed")); | ||
document.body.appendChild(script); | ||
|
||
this.isProcessingCSV = isCSV; | ||
this.isProcessingKML = isKML; | ||
}); | ||
} | ||
|
||
// handle data once it's fetched | ||
handleMeetingsData(meetings, isCSV, isKML) { | ||
if (isCSV) { | ||
this.exportCSV(meetings); | ||
} | ||
if (isKML) { | ||
this.exportKML(meetings); | ||
} | ||
} | ||
|
||
// CSV export functionality | ||
exportCSV(meetings) { | ||
const csvContent = `data:text/csv;charset=utf-8,${encodeURIComponent( | ||
convertToCSV(meetings) | ||
this.constructor.convertToCSV(meetings) | ||
)}`; | ||
const downloadLink = document.getElementById("downloadLink"); | ||
downloadLink.href = csvContent; | ||
downloadLink.style.display = "block"; | ||
} | ||
|
||
if (isProcessingKML) { | ||
// KML export functionality | ||
exportKML(meetings) { | ||
const kmlContent = `data:text/xml;charset=utf-8,${encodeURIComponent( | ||
convertToKML(meetings) | ||
this.convertToKML(meetings) | ||
)}`; | ||
const kmlDownloadLink = document.getElementById("kmlDownloadLink"); | ||
kmlDownloadLink.href = kmlContent; | ||
kmlDownloadLink.style.display = "block"; | ||
} | ||
}; | ||
|
||
const convertToCSV = (data) => { | ||
const csvRows = []; | ||
const keys = Object.keys(data[0]); | ||
|
||
// header row | ||
csvRows.push(keys.join(",")); | ||
|
||
data.forEach((row) => { | ||
const values = keys.map((key) => { | ||
let value = row[key]; | ||
if (typeof value === "string") { | ||
// Escape double quotes and wrap in double quotes if it contains a comma | ||
if (value.includes(",") || value.includes('"')) { | ||
// Convert data to CSV | ||
static convertToCSV(data) { | ||
const csvRows = []; | ||
const keys = Object.keys(data[0]); | ||
csvRows.push(keys.join(",")); | ||
|
||
data.forEach((row) => { | ||
const values = keys.map((key) => { | ||
let value = row[key]; | ||
if ( | ||
typeof value === "string" && | ||
(value.includes(",") || value.includes('"')) | ||
) { | ||
value = `"${value.replace(/"/g, '""')}"`; | ||
} | ||
} | ||
return value; | ||
return value; | ||
}); | ||
csvRows.push(values.join(",")); | ||
}); | ||
|
||
csvRows.push(values.join(",")); | ||
}); | ||
|
||
return csvRows.join("\n"); | ||
}; | ||
return csvRows.join("\n"); | ||
} | ||
|
||
const convertToKML = (data) => { | ||
const kmlHeader = ` | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
// Convert data to KML | ||
convertToKML(data) { | ||
let kmlContent = `<?xml version="1.0" encoding="UTF-8"?> | ||
<kml xmlns="http://www.opengis.net/kml/2.2"> | ||
<Document> | ||
`; | ||
|
||
const kmlFooter = ` | ||
<Document>`; | ||
|
||
const placemarks = data | ||
.map((meeting) => { | ||
const name = meeting["meeting_name"].trim() || "NA Meeting"; | ||
const lng = parseFloat(meeting["longitude"]); | ||
const lat = parseFloat(meeting["latitude"]); | ||
if (!lng || !lat) return ""; | ||
|
||
const description = this.constructor.prepareSimpleLine(meeting); | ||
const address = this.constructor.prepareSimpleLine(meeting, false); | ||
|
||
return ` <Placemark> | ||
<name>${name}</name> | ||
${address ? `<address>${address}</address>` : ""} | ||
${description ? `<description>${description}</description>` : ""} | ||
<Point> | ||
<coordinates>${lng},${lat}</coordinates> | ||
</Point> | ||
</Placemark>`; | ||
}) | ||
.join("\n"); | ||
|
||
kmlContent += | ||
placemarks + | ||
` | ||
</Document> | ||
</kml>`; | ||
|
||
const placemarks = data.map((meeting) => { | ||
const name = meeting["meeting_name"].trim() || "NA Meeting"; | ||
const lng = parseFloat(meeting["longitude"]); | ||
const lat = parseFloat(meeting["latitude"]); | ||
|
||
if (lng || lat) { | ||
const description = prepareSimpleLine(meeting); | ||
const address = prepareSimpleLine(meeting, false); | ||
|
||
return ( | ||
` <Placemark>\n` + | ||
` <name>${name}</name>\n` + | ||
(address ? ` <address>${address}</address>\n` : "") + | ||
(description | ||
? ` <description>${description}</description>\n` | ||
: "") + | ||
` <Point>\n` + | ||
` <coordinates>${lng},${lat}</coordinates>\n` + | ||
` </Point>\n` + | ||
` </Placemark>\n` | ||
); | ||
} | ||
}); | ||
return kmlContent; | ||
} | ||
|
||
return kmlHeader + placemarks.join("\n") + kmlFooter; | ||
}; | ||
// KML and CSV data preparation | ||
static prepareSimpleLine(meeting, withDate = true) { | ||
const getLocationInfo = () => { | ||
const locationInfo = []; | ||
const addInfo = (property) => { | ||
if (property in meeting) { | ||
const value = meeting[property].trim(); | ||
if (value) { | ||
locationInfo.push(value); | ||
} | ||
} | ||
}; | ||
|
||
addInfo("location_text"); | ||
addInfo("location_street"); | ||
addInfo("location_city_subsection"); | ||
addInfo("location_municipality"); | ||
addInfo("location_neighborhood"); | ||
addInfo("location_province"); | ||
addInfo("location_postal_code_1"); | ||
addInfo("location_nation"); | ||
addInfo("location_info"); | ||
|
||
return locationInfo.join(", "); | ||
}; | ||
|
||
const exportData = () => { | ||
const query = document.getElementById("query").value; | ||
if (!query.includes("/client_interface/jsonp")) { | ||
alert("Invalid BMLT query URL, must use jsonp endpoint."); | ||
return; | ||
} | ||
// Only support GetSearchResults for KML | ||
const isKML = query.includes("GetSearchResults"); | ||
fetchMeetings(query, handleMeetingsData, true, isKML); | ||
if (isKML) { | ||
fetchMeetings(query, handleMeetingsData, true, true); | ||
} | ||
}; | ||
|
||
const prepareSimpleLine = (meeting, withDate = true) => { | ||
const getLocationInfo = () => { | ||
const locationInfo = []; | ||
const addInfo = (property) => { | ||
if (property in meeting) { | ||
const value = meeting[property].trim(); | ||
if (value) { | ||
locationInfo.push(value); | ||
const getDateString = () => { | ||
const weekday_strings = [ | ||
"All", | ||
"Sunday", | ||
"Monday", | ||
"Tuesday", | ||
"Wednesday", | ||
"Thursday", | ||
"Friday", | ||
"Saturday", | ||
]; | ||
const weekday = parseInt(meeting["weekday_tinyint"].trim()); | ||
const weekdayString = weekday_strings[weekday]; | ||
|
||
const startTime = `2000-01-01 ${meeting["start_time"]}`; | ||
const time = new Date(startTime); | ||
|
||
if (weekdayString && withDate) { | ||
let dateString = weekdayString; | ||
|
||
if (!isNaN(time)) { | ||
dateString += `, ${time.toLocaleTimeString("en-US", { | ||
hour: "numeric", | ||
minute: "numeric", | ||
hour12: true, | ||
})}`; | ||
} | ||
|
||
return dateString; | ||
} | ||
|
||
return ""; | ||
}; | ||
|
||
addInfo("location_text"); | ||
addInfo("location_street"); | ||
addInfo("location_city_subsection"); | ||
addInfo("location_municipality"); | ||
addInfo("location_neighborhood"); | ||
addInfo("location_province"); | ||
addInfo("location_postal_code_1"); | ||
addInfo("location_nation"); | ||
addInfo("location_info"); | ||
|
||
return locationInfo.join(", "); | ||
}; | ||
|
||
const getDateString = () => { | ||
const weekday_strings = [ | ||
"All", | ||
"Sunday", | ||
"Monday", | ||
"Tuesday", | ||
"Wednesday", | ||
"Thursday", | ||
"Friday", | ||
"Saturday", | ||
]; | ||
const weekday = parseInt(meeting["weekday_tinyint"].trim()); | ||
const weekdayString = weekday_strings[weekday]; | ||
|
||
const startTime = `2000-01-01 ${meeting["start_time"]}`; | ||
const time = new Date(startTime); | ||
|
||
if (weekdayString && withDate) { | ||
let dateString = weekdayString; | ||
|
||
if (!isNaN(time)) { | ||
dateString += `, ${time.toLocaleTimeString("en-US", { | ||
hour: "numeric", | ||
minute: "numeric", | ||
hour12: true, | ||
})}`; | ||
} | ||
const locationInfo = getLocationInfo(); | ||
const dateString = getDateString(); | ||
|
||
if (withDate && dateString && locationInfo) { | ||
return `${dateString}, ${locationInfo}`; | ||
} else if (dateString) { | ||
return dateString; | ||
} else { | ||
return locationInfo; | ||
} | ||
} | ||
|
||
return ""; | ||
}; | ||
|
||
const locationInfo = getLocationInfo(); | ||
const dateString = getDateString(); | ||
|
||
if (withDate && dateString && locationInfo) { | ||
return `${dateString}, ${locationInfo}`; | ||
} else if (dateString) { | ||
return dateString; | ||
} else { | ||
return locationInfo; | ||
// start the export process | ||
exportData(query) { | ||
if (!query.includes("/client_interface/jsonp")) { | ||
alert("Invalid BMLT query URL, must use jsonp endpoint."); | ||
return; | ||
} | ||
const isCSV = true; | ||
const isKML = query.includes("GetSearchResults"); | ||
this.fetchMeetings(query, isCSV, isKML).catch((error) => | ||
console.error("Error fetching meetings:", error) | ||
); | ||
} | ||
}; | ||
} | ||
|
||
function exportData() { | ||
const query = document.getElementById("query").value; | ||
const processor = new MeetingDataProcessor(); | ||
processor.exportData(query); | ||
} |