1// Waits for DOM to load then fetches trail data and messages 2document.addEventListener("DOMContentLoaded", (event) => { 3 console.log("DOM fully loaded and parsed"); 4 5 getBikeTrailData(1); 6 getBikeTrailData(2); 7 getBikeTrailData(3); 8 getBikeTrailData(4); 9 getBikeTrailData(5); 10 getBikeTrailData(6); 11 getBikeTrailData(7); 12 getBikeTrailData(8); 13 getBikeTrailData(9); 14 getBikeTrailData(10); 15 getMessage(); 16}); 17 18// MAP 19let mymap = L.map("mapid").setView([47.6062, -122.3321], 12); 20 21L.tileLayer( 22 "https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}", 23 { 24 attribution: 25 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', 26 maxZoom: 18, 27 id: "mapbox/streets-v11", 28 tileSize: 512, 29 zoomOffset: -1, 30 // put mapbox access token here 31 accessToken: 32 "pk.eyJ1IjoibWF0dHNlZDE5OTEiLCJhIjoiY2todHg5bTd3MHpiYTMwbXkzdTNxbGRudCJ9.ThS7ZuUuMHb_rW8Z-GDUMg", 33 } 34).addTo(mymap); 35 36// MAP COUNTER MARKERS 37const burke_gilman_counter = L.marker([47.679359, -122.263684]).addTo(mymap); 38burke_gilman_counter.bindPopup("Burke Gilman Bike Counter"); 39const elliott_bay_counter = L.marker([47.618844, -122.359963]).addTo(mymap); 40elliott_bay_counter.bindPopup("Elliott Bay Bike Counter"); 41const ship_canal_counter = L.marker([47.648119, -122.349855]).addTo(mymap); 42ship_canal_counter.bindPopup("Ship Canal Bike Counter"); 43const mts_counter = L.marker([47.590748, -122.288702]).addTo(mymap); 44mts_counter.bindPopup("Mountain To Sound Bike Counter"); 45const wsb_counter = L.marker([47.571422, -122.349617]).addTo(mymap); 46wsb_counter.bindPopup("West Seattle Bridge Bike Counter"); 47const cs_counter = L.marker([47.527844, -122.280171]).addTo(mymap); 48cs_counter.bindPopup("Chief Sealth Bike Counter"); 49const second_ave_counter = L.marker([47.604777, -122.334661]).addTo(mymap); 50second_ave_counter.bindPopup("Second Avenue Bike Counter"); 51const golden_gardens_counter = L.marker([47.670933, -122.384743]).addTo(mymap); 52golden_gardens_counter.bindPopup("Golden Gardens Bike Counter"); 53const bryant_greenway_counter = L.marker([47.673886, -122.285766]).addTo(mymap); 54bryant_greenway_counter.bindPopup("Bryant Greenway Bike Counter"); 55const broadway_counter = L.marker([47.6135, -122.320842]).addTo(mymap);
56broadway_counter.bindPopup("Broadway Bike Counter"); 57 58// WEATHER 59function drawWeather(d) { 60 var celcius = Math.round(parseFloat(d.main.temp) - 273.15); 61 var fahrenheit = Math.round((parseFloat(d.main.temp) - 273.15) * 1.8 + 32); 62 var description = d.weather[0].description; 63 64 document.getElementById("description").innerHTML = description; 65 document.getElementById("temp").innerHTML = fahrenheit + "°"; 66 document.getElementById("location").innerHTML = d.name; 67 68 if (description.indexOf("rain") > 0) { 69 document.querySelector(".weather").className = "rainy"; 70 } else if (description.indexOf("cloud") > 0) { 71 document.querySelector("#weather").className = "cloudy"; 72 } else if (description.indexOf("sunny") > 0) { 73 document.querySelector(".weather").className = "sunny"; 74 } 75} 76 77function weatherBalloon() { 78 var key = "0adec15f5303cb5a41696c127460d8f9"; 79 fetch( 80 "https://api.openweathermap.org/data/2.5/weather?q=Seattle&appid=" + key 81 ) 82 .then(function (resp) { 83 return resp.json(); 84 }) // Convert data to json 85 .then(function (data) { 86 drawWeather(data); 87 }) 88 .catch(function () { 89 // catch any errors 90 }); 91} 92 93window.onload = function () { 94 weatherBalloon("Seattle,WA"); 95}; 96 97//MESSAGES 98const msgTabs = document.querySelector("#associated-trails"); 99const msgSection = document.querySelector("#messages"); 100const form = document.querySelector(".message-form"); 101 102// FETCHES MESSAGES FROM BACKEND 103function getMessage() { 104 fetch("https://shrouded-temple-24311.herokuapp.com/messages") 105 .then((resp) => resp.json()) 106 .then((json) => json.forEach((message) => renderMessage(message))); 107} 108 109// RENDERS MESSAGE CARD, APPENDS TO DOM 110function renderMessage(message) { 111 const cardBlock = document.querySelector(".card-block"); 112 113 const card = document.createElement("div"); 114 card.className = "card"; 115 116 card.id = `msg-${message.bike_trail_id}`; 117 card.style.display = "none"; 118 119 const row = document.createElement("div"); 120 row.className = "row"; 121 122 const userCol = document.createElement("div"); 123 userCol.className = "col-sm-4"; 124 userCol.innerText = message.user_name; 125 126 const msgCol = document.createElement("div"); 127 msgCol.className = "col-sm-6"; 128 msgCol.innerText = message.content; 129 130 const messageId = document.createElement("div"); 131 messageId.hidden = true; 132 messageId.id = message.id; 133 134 row.append(userCol, msgCol); 135 card.appendChild(messageId); 136 card.appendChild(row); 137 cardBlock.appendChild(card); 138} 139 140// ADDS DELETE BUTTON TO NEW MESSAGE 141function addDeleteButton(message) { 142 const deleteBtn = document.createElement("button"); 143 deleteBtn.innerText = "Delete"; 144 deleteBtn.className = "btn btn-danger btn-sm col-sm-2"; 145 message.children[1].appendChild(deleteBtn); 146 deleteBtn.addEventListener("click", function (e) { 147 deleteMessage(message); 148 message.remove(); 149 }); 150} 151 152// DELETE FETCH REQ 153function deleteMessage(message) { 154 fetch( 155 `https://shrouded-temple-24311.herokuapp.com/messages/${message.children[0].id}`, 156 { 157 method: "DELETE", 158 } 159 ) 160 .then((response) => response.json()) 161 .then((json) => { 162 return json; 163 }); 164} 165 166// ADDS LISTENER TO POST BUTTON 167const postBtn = document.querySelector("#message-button"); 168postBtn.addEventListener("click", (e) => { 169 e.preventDefault(); 170 if (form.bike_trail_id.id === "") { 171 alert("Please select a bike trail"); 172 } else { 173 let userInput = document.querySelector(".user_name-input").value; 174 let msgInput = document.querySelector(".message-input").value; 175 const newTrail = parseInt(`${e.target.parentNode.children[0].id}`); 176 const newMsg = { 177 user_name: userInput, 178 content: msgInput, 179 bike_trail_id: newTrail, 180 }; 181 document.querySelector(".user_name-input").value = ""; 182 document.querySelector(".message-input").value = ""; 183 postMessage(userInput, msgInput, newTrail); 184 } 185}); 186 187// POST FETCH REQ 188function postMessage(newUser, newMsg, newTrail) { 189 fetch("https://shrouded-temple-24311.herokuapp.com/messages", { 190 method: "POST", 191 headers: { 192 "Content-Type": "application/json", 193 Accept: "application/json ", 194 }, 195 body: JSON.stringify({ 196 user_name: newUser, 197 content: newMsg, 198 bike_trail_id: newTrail, 199 }), 200 }) 201 .then((resp) => resp.json()) 202 .then((json) => { 203 renderMessage(json); 204 const newMessage = document.getElementById(`${json.id}`).parentNode; 205 newMessage.style.display = "block"; 206 addDeleteButton(newMessage); 207 }); 208} 209 210// TRAIL DATA 211const trailData = document.querySelector("#trail-data"); 212const pathSelector = document.querySelector("#path-selector"); 213const trailInfo = document.querySelector("#trail-info"); 214 215// fetch bike trail data 216function getBikeTrailData(bikeTrailId) { 217 fetch(
218 "https://shrouded-temple-24311.herokuapp.com/bike_trails/" + bikeTrailId 219 ) 220 .then((resp) => resp.json()) 221 .then((json) => renderBikeTrailData(json)); 222} 223 224// RENDERS BIKE TRAIL DATA CARDS 225function renderBikeTrailData(data) { 226 // parses counter data api 227 const trailName = data["data"]["attributes"]["name"]; 228 const trailDistance = data["data"]["attributes"]["distance"]; 229 const trailType = data["data"]["attributes"]["trail_type"]; 230 const trailCard = document.createElement("div"); 231 trailCard.id = `card-${data["data"]["id"]}`; 232 const innerTrailInfo = document.createElement("div"); 233 234 // creates "Report a Problem" button 235 const trailProblemRow = document.createElement("row"); 236 const trailProblemBtn = document.createElement("button"); 237 trailProblemBtn.innerText = "Report a Problem"; 238 trailProblemBtn.className = "btn btn-primary btn-block btn-lg"; 239 trailProblemBtn.addEventListener("click", () => { 240 document.location.href = 241 "https://seattle-cwiprod.motorolasolutions.com/cwi/select"; 242 }); 243 trailProblemRow.appendChild(trailProblemBtn); 244 245 // trail name 246 const trailHeader = document.createElement("h1"); 247 trailHeader.innerText = `${trailName}`; 248 trailHeader.className = `${data["data"]["id"]}`; 249 250 // trail info 251 const trailInfoDistance = document.createElement("h2"); 252 trailInfoDistance.className = "col-sm-6"; 253 trailInfoDistance.innerText = `${trailDistance} Miles`; 254 const trailInfoType = document.createElement("h2"); 255 trailInfoType.className = "col-sm-6"; 256 trailInfoType.innerText = trailType; 257 trailCard.style.display = "none"; 258 259 // appends to DOM element 260 innerTrailInfo.append(trailInfoDistance, trailInfoType, trailProblemRow); 261 trailCard.appendChild(trailHeader); 262 trailCard.appendChild(innerTrailInfo); 263 trailInfo.append(trailCard); 264 265 // creates dropdown menu option 266 const trailNameDropdown = document.createElement("a"); 267 trailNameDropdown.innerText = trailName; 268 trailNameDropdown.id = `${data["data"]["id"]}`; 269 trailNameDropdown.className = "dropdown-item"; 270 trailNameDropdown.setAttribute( 271 "longitude", 272 data["data"]["attributes"]["locations"][0]["longitude"] 273 ); 274 trailNameDropdown.setAttribute( 275 "latitude", 276 data["data"]["attributes"]["locations"][0]["latitude"] 277 ); 278 addListenerToDropdownItem(trailNameDropdown); 279 pathSelector.appendChild(trailNameDropdown); 280 281 // creates data chart 282 const chartContainer = document.createElement("div"); 283 chartContainer.style = 284 "height: 290px; width: 90%; right: 40px; position: absolute;"; 285 chartContainer.id = `chartContainer-${data["data"]["id"]}`; 286 trailData.appendChild(chartContainer); 287 const monthNames = [ 288 "January", 289 "February", 290 "March", 291 "April", 292 "May", 293 "June", 294 "July", 295 "August", 296 "September", 297 "October", 298 "November", 299 "December", 300 ]; 301 let dateTime = `${data["included"][0]["attributes"]["date_time"]})`; 302 let yearMonth = dateTime.split("-", 2); 303 let year = yearMonth[0]; 304 let month = parseInt(yearMonth[1]); 305 let monthValue = monthNames[month - 1]; 306 307 // creates data points 308 let trailDataPoints = []; 309 data["included"].forEach((point) => { 310 let dataObj = { 311 label: point["attributes"]["date_time"], 312 y: point["attributes"]["total_trips"], 313 }; 314 trailDataPoints.push(dataObj); 315 }); 316 let chart = new CanvasJS.Chart(`chartContainer-${data["data"]["id"]}`, { 317 theme: "light2", 318 zoomEnabled: true, 319 rangeChanging: function (e) { 320 //update total count 321 var eventCountElement = document.getElementById("eventCount"); 322 eventCountElement.setAttribute( 323 "value", 324 parseInt(eventCountElement.getAttribute("value")) + 1 325 ); 326 327 // update event Trigger 328 var triggerLogElement = document.getElementById("triggerLog"); 329 triggerLogElement.setAttribute("value", e.trigger); 330 }, 331 title: { 332 text: `${monthValue} ${year} Traffic`, 333 }, 334 data: [ 335 { 336 type: "line", 337 dataPoints: trailDataPoints, 338 }, 339 ], 340 axisX: { 341 crosshair: { 342 enabled: true, 343 labelFormatter: (e) => { 344 return ""; 345 }, 346 }, 347 gridThickness: 0, 348 tickLength: 0, 349 lineThickness: 0, 350 title: "Date-Time", 351 labelFontColor: "transparent", 352 }, 353 }); 354 chart.render(); 355 chartContainer.style.display = "none"; 356} 357
358// BIKE PATH LISTENERS AND SELECTORS 359function addListenerToDropdownItem(item) { 360 item.addEventListener("click", (e) => { 361 let infoChildren = trailInfo.children; 362 for (let i = 0; i < infoChildren.length; i++) { 363 let tableChild = infoChildren[i]; 364 tableChild.style.display = "none"; 365 } 366 let chartChildren = trailData.children; 367 for (let i = 0; i < chartChildren.length; i++) { 368 let tableChild = chartChildren[i]; 369 tableChild.style.display = "none"; 370 } 371 const cardBlock = document.querySelector(".card-block"); 372 let messageChildren = cardBlock.children; 373 for (let i = 0; i < messageChildren.length; i++) { 374 let messageChild = messageChildren[i]; 375 messageChild.style.display = "none"; 376 } 377 378 const card = document.getElementById(`card-${e.target.id}`); 379 card.style.display = "block"; 380 const chart = document.getElementById(`chartContainer-${e.target.id}`); 381 chart.style.display = "block"; 382 const trailMessages = document.querySelectorAll(`#msg-${e.target.id}`); 383 trailMessages.forEach((message) => { 384 message.style.display = "block"; 385 }); 386 const hiddenInput = document.querySelector(".message-form").childNodes[1] 387 .children[0]; 388 hiddenInput.id = `${item.id}`; 389 390 // zooms map to bike trail counter location 391 const longitude = parseFloat(e.target.attributes.longitude.value); 392 const latitude = parseFloat(e.target.attributes.latitude.value); 393 mymap.flyTo([longitude, latitude], 14); 394 }); 395}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.