PageSourceSearch

https://seattle-bike-traffic.netlify.app/index.js

js seattle-bike-traffic.netlify.app collected 2026-10-03 11:22:41 UTC 13,004 bytes, 395 lines download raw bytes

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 &copy; <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 + "&deg;";
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.