1(function(global) { 2 "use strict"; 3 4 5 /* 6 * WeatherForecastWidget - encapsulates data retrieval and display for the Weather Forecast Widget 7 * initConfig - configuration instance passed from the attach function 8 */ 9 var WeatherForecastWidget=function(initConfig) { 10 11 //the root HTML tag selector 12 this.rawSelector=initConfig.selector; 13 if ((!initConfig.location) && localStorage) { 14 initConfig.location=localStorage.getItem("loc"); 15 } 16 if (!initConfig.location) initConfig.location="_auto_"; 17 18 //Initialize the widget using the container parameters 19 this.config={ 20 "location":initConfig.location, //initial location 21 "title":initConfig.title || (initConfig.location!=="_auto_" && initConfig.location), 22 "unitGroup":initConfig.unitGroup || "us", //initial location 23 "key":initConfig.apiKey, //api key 24 "hourly":false, 25 "forecastDays":initConfig.forecastDays || 5, 26 "showTitle":!(initConfig.showTitle===false), 27 "showCurrent":!(initConfig.showCurrent===false) 28 } 29 30 //weather forecast data populated by the Weather API calls 31 this.dailydata=null; 32 this.hourlydata=null; 33 this.error=null; 34 var me=this; 35 36 //setLocation - updates the location and triggers a data reload 37 this.setLocation=function(location) { 38 if (!location) location="_auto_"; 39 me.config.location=location; 40 me.dailydata=null; 41 me.hourlydata=null; 42 me.loadForecastData(); 43 } 44 45 this.init=function(stylesheets) { 46 47 if (!stylesheets || stylesheets.length==0) { 48 me.loadForecastData(); 49 return; 50 } 51 var link = document.createElement( 'link' ); 52 link.rel = 'stylesheet'; 53 link.href = stylesheets.pop(); 54 55 document.head.appendChild( link ); 56 link.onload = function() { 57 me.init(stylesheets); 58 }; 59 } 60 61 62 //constructs Weather API request and then loads the weather forecast data from the Weather API 63 this.loadForecastData=function() { 64 //for now abandon loading data if an error has been recorded 65 if ( me.error) return; 66 67 68 if ((me.config.hourly && me.hourlydata) || (!me.config.hourly && me.dailydata)) return; 69 70 //endpoint 71 var uri=(initConfig.root || "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/weatherdata/"); 72 uri+='forecast?'; 73 //parameters 74 uri+="unitGroup="+me.config.unitGroup+"&contentType=json&locationMode=single&iconSet=icons1&location="+me.config.location+"&key="+me.config.key; 75 uri+="&aggregateHours=12" 76 77 var request = new XMLHttpRequest(); 78 request.open('GET', uri, true); 79 80 request.onload = function() { 81 if (this.status >= 200 && this.status < 400) { 82 // Success! 83 var rawResult = JSON.parse(this.response); 84 me.dailydata=rawResult; 85 if (!rawResult || rawResult.errorCode) { 86 me.showError("Error loading data", rawResult.message); 87 if (rawResult) console.log("Error loading data (1):"+rawResult.message); 88 return; 89 } 90 91 me.processValues(rawResult); 92 if (me.config.hourly) { 93 me.hourlydata=rawResult; 94 } else { 95 me.dailydata=rawResult; 96 } 97 //refresh the display 98 me.refresh(); 99 } else { 100 // We reached our target server, but it returned an error 101 102 } 103 }; 104 105 request.onerror = function() { 106 // There was a connection error of some sort 107 }; 108 109 request.send(); 110 111 112 113 } 114 //checks the forecast values and create Date instances for the data time values 115 this.processValues=function(data) { 116 var forecastValues=me.getForecastValues(data); 117 if (!forecastValues) return;
118 var current=new Date(); 119 var offset=current.getTimezoneOffset()*60000; 120 forecastValues.forEach(function(d) { 121 d.datetime=new Date(d.datetime+offset ); 122 }); 123 } 124 125 //extracts the array of forecast values representing each time period in the data 126 this.getForecastValues=function(data) { 127 if (!data) { 128 throw "No data available for "+me.config.location; 129 } 130 var location=data.location; 131 132 if (!location) { 133 throw "No locations found in data for "+me.config.location; 134 } 135 136 if (location.address) { 137 if (localStorage) localStorage.setItem("loc", location.address); 138 me.config.location=location.address; 139 } 140 var forecastValues=location.values; 141 forecastValues=forecastValues.filter(function(d) { 142 return d && (d.temp || d.temp===0); 143 }); 144 return forecastValues; 145 } 146 147 this.refresh=function() { 148 149 me.loadForecastData(); 150 //var skycons = new Skycons(); 151 152 var root=document.querySelector(me.rawSelector); 153 154 me.setHtml(root,null, "<div class='visualcrossing-wx-widget'>"+ 155 "<div class='title'></div>"+ 156 "<div class='content'>"+ 157 "<div class='current'>"+ 158 //"<img class='icon' src=''></img>"+ 159 "<i class='icon wi'></i>"+ 160 "<div class='summary'>"+ 161 "<div class='temp'>-</div>"+ 162 "</div>"+ 163 "</div>"+ 164 "<div class='forecast'></div>"+ 165 "</div>"+ 166 "<a class='credit' href='https://www.visualcrossing.com/weather-data' title='Visual Crossing Weather Data' target='_blank'>Visual Crossing Weather</a>"+ 167 "</div>"); 168 169 var location=me.dailydata.location; 170 var currentConditions=location.currentConditions; 171 var widgetElement=root.querySelector(".visualcrossing-wx-widget"); 172 173 if (me.config.showTitle) { 174 me.setHtml(widgetElement, ".title", me.config.title || location.address); 175 } else { 176 me.toggleClass(widgetElement, ".title","hidden"); 177 } 178 179 if (me.config.showCurrent) { 180 181 182 if (currentConditions) { 183 //skycons.add(widgetElement.querySelector(".current .icon"), currentConditions.icon); 184 //widgetElement.querySelector(".current .icon").setAttribute("src", "icons1/"+currentConditions.icon+".svg"); 185 me.toggleClass(widgetElement, ".current .icon","wi-forecast-io-"+currentConditions.icon); 186 187 var current=widgetElement.querySelector(".current .summary"); 188 me.setHtml(current, ".temp", me.formatTemp(currentConditions.temp, true)); 189 me.setHtml(current, ".precip", currentConditions.precip); 190 } 191 } else { 192 me.toggleClass(widgetElement, ".current","hidden"); 193 } 194 195 196 197 198 var values=location.values; 199 var maxValues=me.config.forecastDays*2; 200 var forecast=widgetElement.querySelector(".forecast"); 201 202 203 for (var i=0;i<values.length;i++) { 204 var d=values[i]; 205 var nextd=(i<values.length-1)?values[i+1]:null; 206 207 if (maxValues && i>=maxValues) break; 208 209 var formattedDate= me.formatDate(d); 210 var nextformattedDate=nextd && me.formatDate(nextd); 211 212 forecast.insertAdjacentHTML('beforeend', "<div class='period'>"+ 213 "<div class='date'></div>"+ 214 "<i class='wi icon'></i>"+ 215 "<div class='maxt'></div>"+ 216 "</div>"); 217 var periodElement=forecast.lastElementChild; 218 219 220 221 var collapseNext=false; 222 223 var isDay=false; 224 225 226 if (nextd && nextformattedDate===formattedDate) { 227 isDay=true; 228 collapseNext=true; 229 i++; 230 } 231 232 233 234 me.setHtml(periodElement, ".date", formattedDate); 235 //periodElement.querySelector(".icon").setAttribute("src", "icons1/"+d.icon+".svg"); 236 me.toggleClass(periodElement, ".icon","wi-forecast-io-"+d.icon); 237 238 if (d.maxt) { 239 me.setHtml(periodElement, ".maxt", me.formatTemp(d.maxt)); 240 } else { 241 me.setHtml(periodElement, ".maxt", me.formatTemp(d.temp)); 242 243 } 244 245 246 } 247 //skycons.play(); 248 249 250 } 251 this.getCompassDirection=function(degrees) { 252 var wdir=Math.round(degrees/22.5);
253 if (wdir<0) wdir+=16; 254 if (wdir>15) wdir-=16; 255 return ["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"][wdir]; 256 } 257 258 this.formatTemp=function(value, showUnit) { 259 if (!value && value!==0) return "-"; 260 value=Math.round(value); 261 if (!showUnit) return value; 262 return value+(me.config.unitGroup==="us"?"F":"C"); 263 } 264 this.formatPrecip=function(value) { 265 if (!value && value!==0) return "-"; 266 return value+(me.config.unitGroup==="us"?"\"":"mm"); 267 } 268 this.formatDate=function(value) { 269 var today=new Date(); 270 var date=new Date(value.datetime); 271 if (today.getDate()===date.getDate()) return "Today"; 272 return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][ date.getDay()]; 273 //return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][ date.getDay()]+" "+date.getDate(); 274 //return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][ date.getDay()]; 275 } 276 this.toggleClass=function(element, selector, className) { 277 var el=element.querySelector(selector); 278 el && (el.classList.toggle(className)); 279 } 280 this.setHtml=function(element, selector, content) { 281 element=selector?element.querySelector(selector):element; 282 element && (element.innerHTML = content); 283 284 } 285 } 286 287 288 289 290 /* 291 * Attach the widget code to the widgets on the page 292 * For each widget config found, create an instance of the widget object 293 */ 294 295 var attach=function(config) { 296
297 var instance=new WeatherForecastWidget(config ); 298 instance.init(["https://www.visualcrossing.com/widgets/forecast-simple/weather-forecast-widget-simple.css","https://www.visualcrossing.com/widgets/forecast-simple/css/weather-icons.min.css"]); 299 //instance.loadForecastData(); 300 return instance; 301 } 302 /* 303 * look for the global scope weatherWidgetConfig 304 * This is an array of widget config (one for each widget on the page) 305 */ 306 if (!window.weatherWidgetConfig) { 307 console.error("No weather widget configuration found!"); 308 } else { 309 window.weatherWidgetConfig.forEach(function(config) { 310 attach(config); 311 }) 312 } 313 314 315 316 global.WeatherForecastDisplay=WeatherForecastWidget; 317 318})(this);
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.