-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.js
343 lines (277 loc) · 14.1 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
//generic unit conversion functions
function convertToCelcius(num) {return Math.round((num - 32) * (5/9));}
function convertToKPH(num) {return Math.round(num * 1.609);}
//function courtesy of SO for setting the timezone of a date object
function convertTZ(date, tzString) {
return new Date((typeof date === "string" ? new Date(date) : date).toLocaleString("en-US", {timeZone: tzString}));
}
//global variable to keep track of our loaded json
let results = null;
function findCityState() {
//grabbing city text
let city = document.getElementById("cityInput").value.trim();
//grabbing state text
let state = document.getElementById("stateInput").value.trim();
//verifying user has entered input for both city and state
if(city == "" || state == "") {
setResultDivs("cityStateError");
return;
}
//verifying user has entered a valid state
let validStates = ["alabama","alaska","arizona","arkansas","california","colorado","connecticut",
"delaware","florida","georgia","hawaii","idaho","illinois","indiana","iowa","kansas","kentucky",
"louisiana","maine","maryland","massachusetts","michigan","minnesota","mississippi","missouri",
"montana","nebraska","nevada","new hampshire","new jersey","new mexico","new york","north carolina",
"north dakota","ohio","oklahoma","oregon","pennsylvania","rhode island","south carolina","south dakota",
"tennessee","texas","utah","vermont","virginia","washington","west virginia","wisconsin","wyoming"];
let validAbbreviations = ['AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA','HI','ID','IL','IN','IA','KS',
'KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ','NM','NY','NC','ND','OH','OK','OR',
'PA','RI','SC','SD','TN','TX','UT','VT','VA','WA','WV','WI','WY'];
if(!validStates.includes(state.toLowerCase()) && !validAbbreviations.includes(state.toUpperCase())) {
setResultDivs("stateError");
return;
}
//creating our request since we are using a free plan there is no need to hide the api key
let requestUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" + city + "," + state + "?key=BDYB2F43GCAVB8YW96AWX9SV7";
//making our apiCall
apiCall(requestUrl);
}
//grabs all weather data for a location based on a zip code
function findZip() {
//verifying input
let zip = document.getElementById("zipInput").value;
if(!/^\d{5}(-\d{4})?$/.test(zip)) {
setResultDivs("zipError");
return;
}
//creating our request since we are using a free plan there is no need to hide the api key
let requestUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" + zip + "?key=BDYB2F43GCAVB8YW96AWX9SV7";
//making our apiCall
apiCall(requestUrl);
}
function apiCall(requestUrl) {
//calling the api and getting our data
fetch(requestUrl, {mode: 'cors'})
.then(function(response) {
//checking if api returns successfully
if (!response.ok) setResultDivs("apiError");
else return response.json();
})
.then(function(response) {
//printing out our json object to the console for easy reference
console.log(response);
//updating our divs
results = response;
updateDisplay();
setResultDivs("result");
});
}
//helper functions to set which divs are visible when a user submits a search
function setResultDivs(status) {
//accessing zipErrorElement and determining if we need to show/hide
let zipErrorElement = document.getElementById("zipError");
if(status == "zipError") zipErrorElement.style.display = "block";
else zipErrorElement.style.display = "none";
//accessing cityStateErrorElement and determining if we need to show/hide
let cityStateErrorElement = document.getElementById("cityStateError");
if(status == "cityStateError") cityStateErrorElement.style.display = "block";
else cityStateErrorElement.style.display = "none";
//accessing stateErrorElement and determining if we need to show/hide
let stateErrorElement = document.getElementById("stateError");
if(status == "stateError") stateErrorElement.style.display = "block";
else stateErrorElement.style.display = "none";
//accessing apiErrorElement and determining if we need to show/hide
let apiErrorElement = document.getElementById("apiError");
if(status == "apiError") apiErrorElement.style.display = "block";
else apiErrorElement.style.display = "none";
//accessing resultElement + related setting elements and determing if we need to show/hide
let resultElement = document.getElementById("result");
let hourToggleElement = document.getElementById("hourlyToggle");
let weekToggleElement = document.getElementById("weeklyToggle");
if(status == "result") {
resultElement.style.display = "block";
hourToggleElement.style.display = "flex";
weekToggleElement.style.display = "flex";
//emptying our search fields upon success
document.getElementById("zipInput").value = "";
document.getElementById("cityInput").value = "";
document.getElementById("stateInput").value = "";
}
else {
resultElement.style.display = "none";
hourToggleElement.style.display = "none";
weekToggleElement.style.display = "none";
}
}
//bool which keeps track of the current system to display units in
let isImperial = true;
//changing the units to imperial
function setImperial() {
//changing the variable
if(isImperial) return;
isImperial = true;
//checking we have data loaded in before making call to update display
if(results != null) updateDisplay();
}
//changing the units to metric
function setMetric() {
//changing the variable
if(!isImperial) return;
isImperial = false;
//checking we have data loaded in before making call to update display
if(results != null) updateDisplay();
}
//updating all the elements of our display using data currently stored in results variable
function updateDisplay() {
//updating the address so user knows where data is being pulled from
document.getElementById("apiAddress").innerText = "Showing results for: " + results.resolvedAddress;
//creating a date object set to the timezone of the location
let currentDate = convertTZ(new Date(), results.timezone);
//finding out the current hour for the place being looked up
let currentHour = currentDate.getHours();
//figuring out how many hours until sunset
let sunsetOffset = Number(results.days[0].sunset.slice(0, 2)) - currentHour;
//figuring out when sunrise today and tomorrow are
let todaySunrise = Number(results.days[0].sunrise.slice(0, 2));
let tomorrowSunrise = Number(results.days[1].sunrise.slice(0, 2));
//figuring out how many hours until the NEXT sunrise (could be either tomorrow or today)
let sunriseOffset = 0;
//checking if the sunrise has already happened today
if(todaySunrise - currentHour <= 0) sunriseOffset = (23 - currentHour) + tomorrowSunrise;
else sunriseOffset = todaySunrise - currentHour;
//creating an array of hours to reference for our headers
let hours = ["12am", "1am", "2am", "3am", "4am", "5am", "6am", "7am", "8am", "9am", "10am", "11am",
"12pm", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm", "9pm", "10pm", "11pm"];
//grabbing our hour elements
let hourElements = document.getElementsByClassName("hourData");
//iterating through our hour displays
for(let i = 0; i < 12; i++) {
//accessing the element
let hourInfo = hourElements[i];
//accessing the header
let header = hourInfo.getElementsByClassName("hourHeader")[0];
//figuring out what hour the header should display and whether we are in today or tomorrow
let hourIndex = currentHour + i;
let dayIndex = 0;
if(hourIndex > 23) {
dayIndex = 1;
hourIndex -= 24;
}
//updating our header
header.innerHTML = hours[hourIndex];
//accessing and updating the icon
let hourIcon = hourInfo.getElementsByClassName("containerImage")[0];
//bool in third parameter checks if the current hour is after sunset
updateIcon(hourIcon, results.days[dayIndex].hours[hourIndex].conditions, (i >= sunsetOffset && i < sunriseOffset));
//accessing the wind speed html element
let windSpeedElement = hourInfo.getElementsByClassName("hourWindDiv")[0].getElementsByClassName("hourWindText")[0];
//grabbing the windspeed from the json object
let windSpeedNumber = results.days[dayIndex].hours[hourIndex].windspeed;
//checking for imperial/metric and updating the element
if(isImperial) windSpeedElement.innerHTML = Math.round(windSpeedNumber) + " mph";
else windSpeedElement.innerHTML = convertToKPH(windSpeedNumber) + " kph";
//accessing the temperature html element
let temperatureElement = hourInfo.getElementsByClassName("hourTemp")[0];
//grabbing the temperature from the json object
let temperatureNumber = results.days[dayIndex].hours[hourIndex].temp;
//checking for imperial/metric and updating the element
if(isImperial) temperatureElement.innerHTML = Math.round(temperatureNumber) + "\u00B0";
else temperatureElement.innerHTML = convertToCelcius(temperatureNumber) + "\u00B0";
}
//finding out current day
let currentDay = currentDate.getDay();
//creating an array of days to reference for our headers
let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
//grabbing our day elements
let dayElements = document.getElementsByClassName("dayData");
//iterating through our day displays
for(let i = 0; i < 7; i++) {
//accessing the element
let dayInfo = dayElements[i];
console.log(dayInfo.innerHTML);
//accessing and updating the header text
let header = dayInfo.getElementsByClassName("dayHeader")[0];
header.innerHTML = days[(currentDay + i)%7];
//accessing and updating the icon
let dayWeatherIcon = dayInfo.getElementsByClassName("containerImage")[0];
//bool in third parameter checks if it is currently past sunset for the current day of the week
updateIcon(dayWeatherIcon, results.days[i].conditions, (i == 0 && sunsetOffset <= 0));
//accessing and updating the rain chance text
let rainChanceText = dayInfo.getElementsByClassName("dayRainDiv")[0].getElementsByClassName("dayRainText")[0];
rainChanceText.innerHTML = results.days[i].precipprob + "%";
//accessing the high/low temperature container
let highLowContainer = dayInfo.getElementsByClassName("highLowContainer")[0];
//accessing the high temp element
let highElement = highLowContainer.getElementsByClassName("high")[0];
//grabbing the high temp from the json object
let highNum = results.days[i].tempmax;
//checking for imperial/metric and updating the element
if(isImperial) highElement.innerHTML = "H: " + Math.round(highNum) + "\u00B0";
else highElement.innerHTML = "H: " + convertToCelcius(highNum) + "\u00B0";
//accessing the low temp element
let lowElement = highLowContainer.getElementsByClassName("low")[0];
//grabbing the low temp from the json object
let lowNum = results.days[i].tempmin;
//checking for imperial/metric and updating the element
if(isImperial) lowElement.innerHTML = "L: " + Math.round(lowNum) + "\u00B0";
else lowElement.innerHTML = "L: " + convertToCelcius(lowNum) + "\u00B0";
}
}
//helper function for updating the icons in our display
function updateIcon(element, conditions, isNighttime) {
//if else statement for determinging which icon to use
//would be a switch case but we aren't looking for exact matching
if(conditions.includes("Rain")) element.src = "./assets/rainy.png";
else if(conditions.includes("Overcast")) element.src = "./assets/cloudy.png";
else if(conditions.includes("Partially cloudy")) {
if(isNighttime) element.src = "./assets/partlyCloudyNight.png";
else element.src = "./assets/partlyCloudy.png";
}
else if(conditions.includes("Clear")) {
if(isNighttime) element.src = "./assets/nighty.png"
else element.src = "./assets/sunny.png";
}
else {
//for now just assuming unknown conditions are snowy
element.src = "./assets/snowy.png";
console.log(conditions);
}
}
//variable to keep track of whether the hourly forecase should be displayed
let showHourly = true;
//function for toggling whether the hourly forecast is displayed
function toggleHourly() {
//updating our variable
showHourly = !showHourly;
//grabbing the elements related to hourly forecasts
let hourlyForecastHeader = document.getElementById("hourlyForecastHeader");
let hourlyForecastBox = document.getElementById("hourlyForecastBox");
//showing and hiding the elements as appropriate
if(showHourly) {
hourlyForecastHeader.style.display = "block";
hourlyForecastBox.style.display = "flex";
}
else {
hourlyForecastHeader.style.display = "none";
hourlyForecastBox.style.display = "none";
}
}
//variable to keep track of weather weekly forecast should be displayed
let showWeekly = true;
//function for toggling weekly forecast
function toggleWeekly() {
//updating our variable
showWeekly = !showWeekly;
//grabbing the elements related to hourly forecasts
let weeklyForecastHeader = document.getElementById("weeklyForecastHeader");
let weeklyForecastBox = document.getElementById("weeklyForecastBox");
//showing and hiding the elements as appropriate
if(showWeekly) {
weeklyForecastHeader.style.display = "block";
weeklyForecastBox.style.display = "flex";
}
else {
weeklyForecastHeader.style.display = "none";
weeklyForecastBox.style.display = "none";
}
}