From 960ff44b724aa59db757516154c4084d265a58b8 Mon Sep 17 00:00:00 2001
From: buffbears <54551697+buffbears@users.noreply.github.com>
Date: Thu, 1 Apr 2021 21:40:58 -0400
Subject: [PATCH] Added Extended Forecast, Daypart + general tweaks
---
webroot/audio.js | 231 ++++++++++++++++
webroot/data-manager.js | 114 ++++++++
webroot/date_fns.min.js | 4 +
webroot/groupull.js | 75 +++++
webroot/location.js | 179 ++++++++++++
webroot/loops.js | 522 ++++++++++++++++++++++++++++++++++
webroot/main.js | 34 +++
webroot/radar.js | 188 +++++++++++++
webroot/slides-loop.js | 599 ++++++++++++++++++++++++++++++++++++++++
webroot/utils.js | 113 ++++++++
webroot/weather.js | 109 ++++++++
11 files changed, 2168 insertions(+)
create mode 100644 webroot/audio.js
create mode 100644 webroot/data-manager.js
create mode 100644 webroot/date_fns.min.js
create mode 100644 webroot/groupull.js
create mode 100644 webroot/location.js
create mode 100644 webroot/loops.js
create mode 100644 webroot/main.js
create mode 100644 webroot/radar.js
create mode 100644 webroot/slides-loop.js
create mode 100644 webroot/utils.js
create mode 100644 webroot/weather.js
diff --git a/webroot/audio.js b/webroot/audio.js
new file mode 100644
index 0000000..c8be941
--- /dev/null
+++ b/webroot/audio.js
@@ -0,0 +1,231 @@
+function WeatherAudio() {
+
+ var musicarr = [],
+ $players = $('
'),
+ that=this,
+ mobilePlaying = false;
+
+ (function() {
+
+ $('body').append($players);
+
+ // start the music
+ buildMusicArray(musicarr);
+ shuffle(musicarr);
+
+ startPlaying(musicarr, true);
+
+ function buildMusicArray(arr) {
+
+ var musicpath = "music/";
+
+ // insert track names
+ for (var i = 1; i<66; i++) {
+ arr.push(musicpath + "Track " + String('0'+i).slice(-2) + '.mp3');
+ }
+
+ }
+
+ })();
+
+
+ function startPlaying(arrPlayList, bLoop) {
+
+
+ // only allow one set of players to be created
+ var myclass = (bLoop ? 'music' : 'voice');
+ if ($players.find('.' + myclass).length>0) {return}
+
+ var current=-1,
+ len = arrPlayList.length,
+ $player = initPlayer('p1'),
+ $preloader = initPlayer('p2'),
+ $myplayers = $players.find('.' + myclass);
+
+ // init the event to output ID3 track info if this is a music play
+ if (myclass=='music') {
+ $players.find('.music').bind($.jPlayer.event.play,
+ function() { // event.jPlayer.status.media
+
+ if (that.playCallback) {
+
+ var mp3url = $(this).data('jPlayer').status.src,
+ relativeUrl = mp3url.replace('%20',' ').slice(-arrPlayList[current].length);
+
+ // only trigger on current track, do not do on preload play/stop
+ if (relativeUrl==arrPlayList[current]) {
+
+ ID3.loadTags(mp3url, function() {
+ var tags = ID3.getAllTags(mp3url);
+ that.playCallback(tags); //alert(tags.artist + " - " + tags.title + ", " + tags.album);
+ });
+ }
+ }
+ }
+ );
+ } else {
+ // duck the music if we're going to play a vocal
+ $players.find('.music').jPlayer('volume', 0.30);
+ }
+
+ // prime the preloader
+ $preloader.jPlayer("setMedia", {mp3:arrPlayList[0]});
+
+ playNext();
+
+
+ function playNext() {
+
+ current = nextIndex();
+
+ if (nextIndex()===null) {
+ // nothing to preload so don't fire off a playNext after this play
+ $preloader.unbind($.jPlayer.event.ended);
+ $preloader.bind($.jPlayer.event.ended,
+ function() {
+ $players.find('.music').jPlayer('volume', 0.80); // bring music volume back up
+ $player.remove();
+ $preloader.remove();
+ }
+ );
+ swapAndPlay();
+ } else {
+ swapAndPlay();
+
+ // preload the next track
+ function doPreload(trackname) {
+ try {
+ $preloader.jPlayer("setMedia", {mp3:arrPlayList[nextIndex()]}).jPlayer("play").jPlayer("stop");
+ } catch(e) {
+ setTimeout( function() {doPreload(trackname)}, 500);
+ }
+ }
+ doPreload( arrPlayList[nextIndex()] );
+
+ }
+
+ function nextIndex() {
+ var nextI = current+1;
+ if (nextI
');
+ $div.jPlayer({
+ swfPath: document.baseURI + "jplayer",
+ preload: 'auto',
+ ended: playNext
+ });
+ $players.append($div);
+ return $div;
+ }
+
+
+
+ function swapAndPlay() {
+ // who would think swapping two vars would be so hard?
+ var $temp1 = $player,
+ $temp2 = $preloader;
+ $player = null; $preloader = null;
+ $player=$temp2; $preloader=$temp1;
+
+ $player.jPlayer("play");
+
+ $(document).mousedown( function() {
+ if (!mobilePlaying) {
+ $player.jPlayer("play");
+ mobilePlaying = true;
+ }
+ });
+ }
+
+
+
+ }
+
+
+
+
+ // click for debugging or whater you want
+ $('body').on('click', function(){
+ //$player.jPlayer("playHead", 100);
+ //getWeatherbyLocation();
+ });
+
+
+ this.playCallback = {};
+
+
+ // plays the vocal current conditions announcement
+ this.playCurrentConditions = function (currentConditions) {
+
+ var array2play = ['CC_INTRO' + (Math.floor(Math.random()*2)+1), currentConditions.temp.replace('-', 'M')],
+ condfile = mapCurrentConditions(currentConditions.code);
+
+ if (condfile) { array2play.push(condfile); }
+
+ $.each(array2play, function(i,v){
+ array2play[i] = '/localvocals/' + v + '.mp3';
+ });
+
+ startPlaying(array2play, false);
+
+ function mapCurrentConditions(ccCode){
+ return "CC" + {0:"422",1:"EF300",2:"EF300",3:"422",4:"400",5:"500",6:"600",7:"700",8:"800",9:"901",10:"1000",11:"1100",12:"2680",13:"1601",14:"1312",15:"1500",16:"1600",17:"1730",18:"1800",19:"1939",20:"2000",21:"2000",22:"2200",23:"2410",24:"2410",26:"2600",27:"2700",28:"2700",29:"2900",30:"2900",31:"3100",32:"3200",33:"3400",34:"3400",35:"1730",37:"429",38:"429",39:"429",40:"2680",41:"1402",42:"1312",43:"1402",44:"2900",45:"1100",46:"1312",47:"EF3900"}[ccCode];
+ }
+
+ }
+
+
+ this.playLocalRadar = function() {
+ startPlaying(['/localvocals/RADAR.mp3'], false);
+ }
+
+
+
+}
+var weatherAudio = new WeatherAudio();
+
+//ID3 Reader
+//https://github.com/aadsm/JavaScript-ID3-Reader
+(function(A){if("object"===typeof exports&&"undefined"!==typeof module)module.f=A();else if("function"===typeof define&&define.M)define([],A);else{var g;"undefined"!==typeof window?g=window:"undefined"!==typeof global?g=global:"undefined"!==typeof self?g=self:g=this;g.ID3=A()}})(function(){return function g(l,h,f){function c(b,d){if(!h[b]){if(!l[b]){var e="function"==typeof require&&require;if(!d&&e)return e(b,!0);if(a)return a(b,!0);e=Error("Cannot find module '"+b+"'");throw e.code="MODULE_NOT_FOUND",
+e;}e=h[b]={f:{}};l[b][0].call(e.f,function(a){var e=l[b][1][a];return c(e?e:a)},e,e.f,g,l,h,f)}return h[b].f}for(var a="function"==typeof require&&require,b=0;ba&&(a+=65536);return a};this.m=function(a){var b=this.a(a),d=this.a(a+1),m=this.a(a+2);a=this.a(a+3);b=(((b<<8)+d<<8)+m<<8)+a;0>b&&(b+=4294967296);return b};this.w=function(a){var b=this.a(a),d=this.a(a+1);a=this.a(a+2);b=((b<<8)+d<<8)+a;0>b&&(b+=16777216);return b};this.c=function(a,b){for(var d=[],m=a,c=0;mb&&(b=0);a>=blockTotal&&(a=blockTotal-1);return[b,a]}function g(c,f){for(;n[c[0]];)if(c[0]++,c[0]>c[1]){f&&f();return}for(;n[c[1]];)if(c[1]--,c[0]>c[1]){f&&f();return}var h=[c[0]*m,(c[1]+1)*m-1];b(d,function(a){parseInt(a.getResponseHeader("Content-Length"),10)==e&&(c[0]=0,c[1]=blockTotal-1,h[0]=0,h[1]=e-1);a={data:a.W||a.responseText,offset:h[0]};for(var b=c[0];b<=c[1];b++)n[b]=
+a;f&&f()},a,h,k,!!f)}var k,l=new h("",0,e),n=[];m=m||2048;c="undefined"===typeof c?0:c;blockTotal=~~((e-1)/m)+1;for(var p in l)l.hasOwnProperty(p)&&"function"===typeof l[p]&&(this[p]=l[p]);this.a=function(a){var b;g(f([a,a]));return(b=n[~~(a/m)])&&"string"==typeof b.data?b.data.charCodeAt(a-b.offset)&255:b&&"unknown"==typeof b.data?IEBinary_getByteAt(b.data,a-b.offset):""};this.i=function(a,b){g(f(a),b)}}(function(){d(f,function(a){a=parseInt(a.getResponseHeader("Content-Length"),10)||-1;c(new e(f,
+a))},a)})()}},{"./binaryfile":1,xmlhttprequest:void 0}],3:[function(g,l){var h=g("./binaryfile");l.f=function(f,c){return function(a,b){var m=c||new FileReader;m.onload=function(a){b(new h(a.target.result))};m.readAsBinaryString(f)}}},{"./binaryfile":1}],4:[function(g,l){function h(b){return"ftypM4A"==b.c(4,7)?f:"ID3"==b.c(0,3)?a:c}var f=g("./id4"),c=g("./id3v1"),a=g("./id3v2"),b=g("./bufferedbinaryajax"),m=g("./filereader");"undefined"!==typeof window&&(window.FileAPIReader=m);var d={},e={},r=[0,
+7];d.B=function(a){delete e[a]};d.A=function(){e={}};d.H=function(a,d,c){c=c||{};(c.dataReader||b)(a,function(b){b.i(r,function(){var m=h(b);m.u(b,function(){var f=c.tags,h=m.v(b,f),f=e[a]||{},r;for(r in h)h.hasOwnProperty(r)&&(f[r]=h[r]);e[a]=f;d&&d()})})},c.onError)};d.D=function(a){if(!e[a])return null;var b={},d;for(d in e[a])e[a].hasOwnProperty(d)&&(b[d]=e[a][d]);return b};d.G=function(a,b){return e[a]?e[a][b]:null};d.FileAPIReader=m;d.loadTags=d.H;d.getAllTags=d.D;d.getTag=d.G;d.clearTags=d.B;
+d.clearAll=d.A;l.f=d},{"./bufferedbinaryajax":2,"./filereader":3,"./id3v1":5,"./id3v2":6,"./id4":8}],5:[function(g,l){var h={},f="Blues;Classic Rock;Country;Dance;Disco;Funk;Grunge;Hip-Hop;Jazz;Metal;New Age;Oldies;Other;Pop;R&B;Rap;Reggae;Rock;Techno;Industrial;Alternative;Ska;Death Metal;Pranks;Soundtrack;Euro-Techno;Ambient;Trip-Hop;Vocal;Jazz+Funk;Fusion;Trance;Classical;Instrumental;Acid;House;Game;Sound Clip;Gospel;Noise;AlternRock;Bass;Soul;Punk;Space;Meditative;Instrumental Pop;Instrumental Rock;Ethnic;Gothic;Darkwave;Techno-Industrial;Electronic;Pop-Folk;Eurodance;Dream;Southern Rock;Comedy;Cult;Gangsta;Top 40;Christian Rap;Pop/Funk;Jungle;Native American;Cabaret;New Wave;Psychadelic;Rave;Showtunes;Trailer;Lo-Fi;Tribal;Acid Punk;Acid Jazz;Polka;Retro;Musical;Rock & Roll;Hard Rock;Folk;Folk-Rock;National Folk;Swing;Fast Fusion;Bebob;Latin;Revival;Celtic;Bluegrass;Avantgarde;Gothic Rock;Progressive Rock;Psychedelic Rock;Symphonic Rock;Slow Rock;Big Band;Chorus;Easy Listening;Acoustic;Humour;Speech;Chanson;Opera;Chamber Music;Sonata;Symphony;Booty Bass;Primus;Porn Groove;Satire;Slow Jam;Club;Tango;Samba;Folklore;Ballad;Power Ballad;Rhythmic Soul;Freestyle;Duet;Punk Rock;Drum Solo;Acapella;Euro-House;Dance Hall".split(";");
+h.u=function(c,a){var b=c.l();c.i([b-128-1,b],a)};h.v=function(c){var a=c.l()-128;if("TAG"==c.c(a,3)){var b=c.c(a+3,30).replace(/\0/g,""),m=c.c(a+33,30).replace(/\0/g,""),d=c.c(a+63,30).replace(/\0/g,""),e=c.c(a+93,4).replace(/\0/g,"");if(0==c.a(a+97+28))var h=c.c(a+97,28).replace(/\0/g,""),g=c.a(a+97+29);else h="",g=0;c=c.a(a+97+30);return{version:"1.1",title:b,artist:m,album:d,year:e,comment:h,track:g,genre:255>c?f[c]:""}}return{}};l.f=h},{}],6:[function(g,l){function h(a,c){var d=c.a(a),e=c.a(a+
+1),f=c.a(a+2);return c.a(a+3)&127|(f&127)<<7|(e&127)<<14|(d&127)<<21}var f=g("./id3v2frames");f.frames={BUF:"Recommended buffer size",CNT:"Play counter",COM:"Comments",CRA:"Audio encryption",CRM:"Encrypted meta frame",ETC:"Event timing codes",EQU:"Equalization",GEO:"General encapsulated object",IPL:"Involved people list",LNK:"Linked information",MCI:"Music CD Identifier",MLL:"MPEG location lookup table",PIC:"Attached picture",POP:"Popularimeter",REV:"Reverb",RVA:"Relative volume adjustment",SLT:"Synchronized lyric/text",
+STC:"Synced tempo codes",TAL:"Album/Movie/Show title",TBP:"BPM (Beats Per Minute)",TCM:"Composer",TCO:"Content type",TCR:"Copyright message",TDA:"Date",TDY:"Playlist delay",TEN:"Encoded by",TFT:"File type",TIM:"Time",TKE:"Initial key",TLA:"Language(s)",TLE:"Length",TMT:"Media type",TOA:"Original artist(s)/performer(s)",TOF:"Original filename",TOL:"Original Lyricist(s)/text writer(s)",TOR:"Original release year",TOT:"Original album/Movie/Show title",TP1:"Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
+TP2:"Band/Orchestra/Accompaniment",TP3:"Conductor/Performer refinement",TP4:"Interpreted, remixed, or otherwise modified by",TPA:"Part of a set",TPB:"Publisher",TRC:"ISRC (International Standard Recording Code)",TRD:"Recording dates",TRK:"Track number/Position in set",TSI:"Size",TSS:"Software/hardware and settings used for encoding",TT1:"Content group description",TT2:"Title/Songname/Content description",TT3:"Subtitle/Description refinement",TXT:"Lyricist/text writer",TXX:"User defined text information frame",
+TYE:"Year",UFI:"Unique file identifier",ULT:"Unsychronized lyric/text transcription",WAF:"Official audio file webpage",WAR:"Official artist/performer webpage",WAS:"Official audio source webpage",WCM:"Commercial information",WCP:"Copyright/Legal information",WPB:"Publishers official webpage",WXX:"User defined URL link frame",AENC:"Audio encryption",APIC:"Attached picture",COMM:"Comments",COMR:"Commercial frame",ENCR:"Encryption method registration",EQUA:"Equalization",ETCO:"Event timing codes",GEOB:"General encapsulated object",
+GRID:"Group identification registration",IPLS:"Involved people list",LINK:"Linked information",MCDI:"Music CD identifier",MLLT:"MPEG location lookup table",OWNE:"Ownership frame",PRIV:"Private frame",PCNT:"Play counter",POPM:"Popularimeter",POSS:"Position synchronisation frame",RBUF:"Recommended buffer size",RVAD:"Relative volume adjustment",RVRB:"Reverb",SYLT:"Synchronized lyric/text",SYTC:"Synchronized tempo codes",TALB:"Album/Movie/Show title",TBPM:"BPM (beats per minute)",TCOM:"Composer",TCON:"Content type",
+TCOP:"Copyright message",TDAT:"Date",TDLY:"Playlist delay",TENC:"Encoded by",TEXT:"Lyricist/Text writer",TFLT:"File type",TIME:"Time",TIT1:"Content group description",TIT2:"Title/songname/content description",TIT3:"Subtitle/Description refinement",TKEY:"Initial key",TLAN:"Language(s)",TLEN:"Length",TMED:"Media type",TOAL:"Original album/movie/show title",TOFN:"Original filename",TOLY:"Original lyricist(s)/text writer(s)",TOPE:"Original artist(s)/performer(s)",TORY:"Original release year",TOWN:"File owner/licensee",
+TPE1:"Lead performer(s)/Soloist(s)",TPE2:"Band/orchestra/accompaniment",TPE3:"Conductor/performer refinement",TPE4:"Interpreted, remixed, or otherwise modified by",TPOS:"Part of a set",TPUB:"Publisher",TRCK:"Track number/Position in set",TRDA:"Recording dates",TRSN:"Internet radio station name",TRSO:"Internet radio station owner",TSIZ:"Size",TSRC:"ISRC (international standard recording code)",TSSE:"Software/Hardware and settings used for encoding",TYER:"Year",TXXX:"User defined text information frame",
+UFID:"Unique file identifier",USER:"Terms of use",USLT:"Unsychronized lyric/text transcription",WCOM:"Commercial information",WCOP:"Copyright/Legal information",WOAF:"Official audio file webpage",WOAR:"Official artist/performer webpage",WOAS:"Official audio source webpage",WORS:"Official internet radio station homepage",WPAY:"Payment",WPUB:"Publishers official webpage",WXXX:"User defined URL link frame"};var c={title:["TIT2","TT2"],artist:["TPE1","TP1"],album:["TALB","TAL"],year:["TYER","TYE"],comment:["COMM",
+"COM"],track:["TRCK","TRK"],genre:["TCON","TCO"],picture:["APIC","PIC"],lyrics:["USLT","ULT"]},a=["title","artist","album","track"];f.u=function(a,c){a.i([0,h(6,a)],c)};f.v=function(b,m){var d=0,e=b.a(d+3);if(42.4"};var r=b.a(d+4),g=b.g(d+5,7),l=b.g(d+5,6),w=b.g(d+5,5),x=h(d+6,b),d=d+10;if(l)var q=b.m(d),d=d+(q+4);var e={version:"2."+e+"."+r,major:e,revision:r,flags:{unsynchronisation:g,extended_header:l,experimental_indicator:w},size:x},k;if(g)k={};else{for(var x=x-10,g=b,r=m,
+l={},w=e.major,q=[],u=0,n;n=(r||a)[u];u++)q=q.concat(c[n]||[n]);for(r=q;dr.indexOf(k))){if(2e||224<=e?f[d]=String.fromCharCode(l):
+(e=(h[a+b]<<8)+h[a+g],a+=2,f[d]=String.fromCharCode(l,e))}h=new String(f.join(""));h.j=a;return h},K:function(h,f){var c=0;f=Math.min(f||h.length,h.length);239==h[0]&&187==h[1]&&191==h[2]&&(c=3);for(var a=[],b=0;cg)a[b]=String.fromCharCode(g);else if(194<=g&&224>g){var d=h[c++];a[b]=String.fromCharCode(((g&31)<<6)+(d&63))}else if(224<=g&&240>g){var d=h[c++],e=h[c++];a[b]=String.fromCharCode(((g&255)<<12)+((d&63)<<6)+(e&63))}else if(240<=g&&245>g){var d=
+h[c++],e=h[c++],l=h[c++],g=((g&7)<<18)+((d&63)<<12)+((e&63)<<6)+(l&63)-65536;a[b]=String.fromCharCode((g>>10)+55296,(g&1023)+56320)}}a=new String(a.join(""));a.j=c;return a},I:function(g,f){var c=[];f=f||g.length;for(var a=0;a x.woeid === woeid);
+ }
+
+ this.init = function (searchString) {
+ _locations[0] = new Location();
+
+ $(_locations[0])
+
+ .on('refresh', function(){ $this.trigger('refresh') })
+ .on('ready', function(){
+ $this.trigger('ready:main');
+ })
+ .on('init', initLocations);
+ _locations[0].first = true;
+ _locations[0].init(searchString);
+
+ };
+
+ // kicks off after main location is returned.
+ // create the list of neighboring cities
+ function initLocations(){
+
+ // find reporting stations
+ var observationData = _locations[0].observations(0),
+ lat = observationData.lat,
+ lon = observationData.lon,
+ locList = [];
+
+ // begin the forcast pull
+ _locations[0].initForecasts();
+
+ // get a list of observation stations info
+ $.getJSON('https://api.weather.gov/points/' + lat + ',' + lon + '/stations', function(data) {
+
+ var feature, geo, station, dist;
+ for (var i=0; i < data.features.length; i++) {
+
+ feature = data.features[i];
+ geo = feature.geometry.coordinates;
+ dist = distance(lat, lon, geo[1], geo[0]);
+
+ if (dist < includeRadiusMiles && dist > excludeRadiusMiles) {
+ locList.push({lat: geo[1], long:geo[0], distance:dist, stationUrl:feature.id});
+ }
+ }
+
+ if (locList.length===0) {
+ $this.trigger('allinit');
+ return
+ }
+
+ // sort list by distance
+ locList.sort(function(a, b) {
+ return parseInt(a.distance) - parseInt(b.distance);
+ });
+
+ // set the station for location 0
+ _locations[0].stationUrl = locList[0].stationUrl
+ _locations[0].initNWSObservations();
+
+ // create location objects, get inital pull
+ for(var loc of locList) {
+ loc.location = new Location();
+ $(loc.location).on('init',onLocationInit);
+ loc.location.init(loc.lat+','+loc.long);
+ loc.location.stationUrl = loc.stationUrl;
+ }
+
+ });
+
+ var initCount=0;
+ function onLocationInit() {
+ initCount++;
+ if (initCount===locList.length) {
+ allLocationsInit();
+ }
+ }
+
+
+
+
+ function allLocationsInit() {
+
+ var location, cities=[], city;
+
+ // add locations removing any duplicate cities by name
+ for(var loc of locList) {
+
+ if (_locations.filter(e => e.city == loc.location.city).length === 0) {
+ _locations.push(loc.location);
+ loc.location.initForecasts();
+ loc.location.initNWSObservations();
+ }
+
+ }
+
+ $this.trigger('allinit');
+
+ }
+
+ }
+
+}
diff --git a/webroot/date_fns.min.js b/webroot/date_fns.min.js
new file mode 100644
index 0000000..e88794d
--- /dev/null
+++ b/webroot/date_fns.min.js
@@ -0,0 +1,4 @@
+(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define([],factory);else if(typeof exports==="object")exports["dateFns"]=factory();else root["dateFns"]=factory()})(this,function(){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={exports:{},id:moduleId,loaded:false};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.loaded=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.p="";return __webpack_require__(0)}([function(module,exports,__webpack_require__){module.exports={addDays:__webpack_require__(1),addHours:__webpack_require__(4),addISOYears:__webpack_require__(6),addMilliseconds:__webpack_require__(5),addMinutes:__webpack_require__(14),addMonths:__webpack_require__(15),addQuarters:__webpack_require__(17),addSeconds:__webpack_require__(18),addWeeks:__webpack_require__(19),addYears:__webpack_require__(20),areRangesOverlapping:__webpack_require__(21),closestIndexTo:__webpack_require__(22),closestTo:__webpack_require__(23),compareAsc:__webpack_require__(24),compareDesc:__webpack_require__(25),differenceInCalendarDays:__webpack_require__(12),differenceInCalendarISOWeeks:__webpack_require__(26),differenceInCalendarISOYears:__webpack_require__(27),differenceInCalendarMonths:__webpack_require__(28),differenceInCalendarQuarters:__webpack_require__(29),differenceInCalendarWeeks:__webpack_require__(31),differenceInCalendarYears:__webpack_require__(32),differenceInDays:__webpack_require__(33),differenceInHours:__webpack_require__(34),differenceInISOYears:__webpack_require__(36),differenceInMilliseconds:__webpack_require__(35),differenceInMinutes:__webpack_require__(38),differenceInMonths:__webpack_require__(39),differenceInQuarters:__webpack_require__(40),differenceInSeconds:__webpack_require__(41),differenceInWeeks:__webpack_require__(42),differenceInYears:__webpack_require__(43),distanceInWords:__webpack_require__(44),distanceInWordsStrict:__webpack_require__(49),distanceInWordsToNow:__webpack_require__(50),eachDay:__webpack_require__(51),endOfDay:__webpack_require__(52),endOfHour:__webpack_require__(53),endOfISOWeek:__webpack_require__(54),endOfISOYear:__webpack_require__(56),endOfMinute:__webpack_require__(57),endOfMonth:__webpack_require__(58),endOfQuarter:__webpack_require__(59),endOfSecond:__webpack_require__(60),endOfToday:__webpack_require__(61),endOfTomorrow:__webpack_require__(62),endOfWeek:__webpack_require__(55),endOfYear:__webpack_require__(63),endOfYesterday:__webpack_require__(64),format:__webpack_require__(65),getDate:__webpack_require__(70),getDay:__webpack_require__(71),getDayOfYear:__webpack_require__(66),getDaysInMonth:__webpack_require__(16),getDaysInYear:__webpack_require__(72),getHours:__webpack_require__(74),getISODay:__webpack_require__(75),getISOWeek:__webpack_require__(68),getISOWeeksInYear:__webpack_require__(76),getISOYear:__webpack_require__(7),getMilliseconds:__webpack_require__(77),getMinutes:__webpack_require__(78),getMonth:__webpack_require__(79),getOverlappingDaysInRanges:__webpack_require__(80),getQuarter:__webpack_require__(30),getSeconds:__webpack_require__(81),getTime:__webpack_require__(82),getYear:__webpack_require__(83),isAfter:__webpack_require__(84),isBefore:__webpack_require__(85),isDate:__webpack_require__(3),isEqual:__webpack_require__(86),isFirstDayOfMonth:__webpack_require__(87),isFriday:__webpack_require__(88),isFuture:__webpack_require__(89),isLastDayOfMonth:__webpack_require__(90),isLeapYear:__webpack_require__(73),isMonday:__webpack_require__(91),isPast:__webpack_require__(92),isSameDay:__webpack_require__(93),isSameHour:__webpack_require__(94),isSameISOWeek:__webpack_require__(96),isSameISOYear:__webpack_require__(98),isSameMinute:__webpack_require__(99),isSameMonth:__webpack_require__(101),isSameQuarter:__webpack_require__(102),isSameSecond:__webpack_require__(104),isSameWeek:__webpack_require__(97),isSameYear:__webpack_require__(106),isSaturday:__webpack_require__(107),isSunday:__webpack_require__(108),isThisHour:__webpack_require__(109),isThisISOWeek:__webpack_require__(110),isThisISOYear:__webpack_require__(111),isThisMinute:__webpack_require__(112),isThisMonth:__webpack_require__(113),isThisQuarter:__webpack_require__(114),isThisSecond:__webpack_require__(115),isThisWeek:__webpack_require__(116),isThisYear:__webpack_require__(117),isThursday:__webpack_require__(118),isToday:__webpack_require__(119),isTomorrow:__webpack_require__(120),isTuesday:__webpack_require__(121),isValid:__webpack_require__(69),isWednesday:__webpack_require__(122),isWeekend:__webpack_require__(123),isWithinRange:__webpack_require__(124),isYesterday:__webpack_require__(125),lastDayOfISOWeek:__webpack_require__(126),lastDayOfISOYear:__webpack_require__(128),lastDayOfMonth:__webpack_require__(129),lastDayOfQuarter:__webpack_require__(130),lastDayOfWeek:__webpack_require__(127),lastDayOfYear:__webpack_require__(131),max:__webpack_require__(132),min:__webpack_require__(133),parse:__webpack_require__(2),setDate:__webpack_require__(134),setDay:__webpack_require__(135),setDayOfYear:__webpack_require__(136),setHours:__webpack_require__(137),setISODay:__webpack_require__(138),setISOWeek:__webpack_require__(139),setISOYear:__webpack_require__(10),setMilliseconds:__webpack_require__(140),setMinutes:__webpack_require__(141),setMonth:__webpack_require__(142),setQuarter:__webpack_require__(143),setSeconds:__webpack_require__(144),setYear:__webpack_require__(145),startOfDay:__webpack_require__(13),startOfHour:__webpack_require__(95),startOfISOWeek:__webpack_require__(8),startOfISOYear:__webpack_require__(11),startOfMinute:__webpack_require__(100),startOfMonth:__webpack_require__(146),startOfQuarter:__webpack_require__(103),startOfSecond:__webpack_require__(105),startOfToday:__webpack_require__(147),startOfTomorrow:__webpack_require__(148),startOfWeek:__webpack_require__(9),startOfYear:__webpack_require__(67),startOfYesterday:__webpack_require__(149),subDays:__webpack_require__(150),subHours:__webpack_require__(151),subISOYears:__webpack_require__(37),subMilliseconds:__webpack_require__(152),subMinutes:__webpack_require__(153),subMonths:__webpack_require__(154),subQuarters:__webpack_require__(155),subSeconds:__webpack_require__(156),subWeeks:__webpack_require__(157),subYears:__webpack_require__(158)}},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function addDays(dirtyDate,dirtyAmount){var date=parse(dirtyDate);var amount=Number(dirtyAmount);date.setDate(date.getDate()+amount);return date}module.exports=addDays},function(module,exports,__webpack_require__){var isDate=__webpack_require__(3);var MILLISECONDS_IN_HOUR=36e5;var MILLISECONDS_IN_MINUTE=6e4;var DEFAULT_ADDITIONAL_DIGITS=2;var parseTokenDateTimeDelimeter=/[T ]/;var parseTokenPlainTime=/:/;var parseTokenYY=/^(\d{2})$/;var parseTokensYYY=[/^([+-]\d{2})$/,/^([+-]\d{3})$/,/^([+-]\d{4})$/];var parseTokenYYYY=/^(\d{4})/;var parseTokensYYYYY=[/^([+-]\d{4})/,/^([+-]\d{5})/,/^([+-]\d{6})/];var parseTokenMM=/^-(\d{2})$/;var parseTokenDDD=/^-?(\d{3})$/;var parseTokenMMDD=/^-?(\d{2})-?(\d{2})$/;var parseTokenWww=/^-?W(\d{2})$/;var parseTokenWwwD=/^-?W(\d{2})-?(\d{1})$/;var parseTokenHH=/^(\d{2}([.,]\d*)?)$/;var parseTokenHHMM=/^(\d{2}):?(\d{2}([.,]\d*)?)$/;var parseTokenHHMMSS=/^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/;var parseTokenTimezone=/([Z+-].*)$/;var parseTokenTimezoneZ=/^(Z)$/;var parseTokenTimezoneHH=/^([+-])(\d{2})$/;var parseTokenTimezoneHHMM=/^([+-])(\d{2}):?(\d{2})$/;function parse(argument,dirtyOptions){if(isDate(argument)){return new Date(argument.getTime())}else if(typeof argument!=="string"){return new Date(argument)}var options=dirtyOptions||{};var additionalDigits=options.additionalDigits;if(additionalDigits==null){additionalDigits=DEFAULT_ADDITIONAL_DIGITS}else{additionalDigits=Number(additionalDigits)}var dateStrings=splitDateString(argument);var parseYearResult=parseYear(dateStrings.date,additionalDigits);var year=parseYearResult.year;var restDateString=parseYearResult.restDateString;var date=parseDate(restDateString,year);if(date){var timestamp=date.getTime();var time=0;var offset;if(dateStrings.time){time=parseTime(dateStrings.time)}if(dateStrings.timezone){offset=parseTimezone(dateStrings.timezone)}else{offset=new Date(timestamp+time).getTimezoneOffset();offset=new Date(timestamp+time+offset*MILLISECONDS_IN_MINUTE).getTimezoneOffset()}return new Date(timestamp+time+offset*MILLISECONDS_IN_MINUTE)}else{return new Date(argument)}}function splitDateString(dateString){var dateStrings={};var array=dateString.split(parseTokenDateTimeDelimeter);var timeString;if(parseTokenPlainTime.test(array[0])){dateStrings.date=null;timeString=array[0]}else{dateStrings.date=array[0];timeString=array[1]}if(timeString){var token=parseTokenTimezone.exec(timeString);if(token){dateStrings.time=timeString.replace(token[1],"");dateStrings.timezone=token[1]}else{dateStrings.time=timeString}}return dateStrings}function parseYear(dateString,additionalDigits){var parseTokenYYY=parseTokensYYY[additionalDigits];var parseTokenYYYYY=parseTokensYYYYY[additionalDigits];var token;token=parseTokenYYYY.exec(dateString)||parseTokenYYYYY.exec(dateString);if(token){var yearString=token[1];return{year:parseInt(yearString,10),restDateString:dateString.slice(yearString.length)}}token=parseTokenYY.exec(dateString)||parseTokenYYY.exec(dateString);if(token){var centuryString=token[1];return{year:parseInt(centuryString,10)*100,restDateString:dateString.slice(centuryString.length)}}return{year:null}}function parseDate(dateString,year){if(year===null){return null}var token;var date;var month;var week;if(dateString.length===0){date=new Date(0);date.setUTCFullYear(year);return date}token=parseTokenMM.exec(dateString);if(token){date=new Date(0);month=parseInt(token[1],10)-1;date.setUTCFullYear(year,month);return date}token=parseTokenDDD.exec(dateString);if(token){date=new Date(0);var dayOfYear=parseInt(token[1],10);date.setUTCFullYear(year,0,dayOfYear);return date}token=parseTokenMMDD.exec(dateString);if(token){date=new Date(0);month=parseInt(token[1],10)-1;var day=parseInt(token[2],10);date.setUTCFullYear(year,month,day);return date}token=parseTokenWww.exec(dateString);if(token){week=parseInt(token[1],10)-1;return dayOfISOYear(year,week)}token=parseTokenWwwD.exec(dateString);if(token){week=parseInt(token[1],10)-1;var dayOfWeek=parseInt(token[2],10)-1;return dayOfISOYear(year,week,dayOfWeek)}return null}function parseTime(timeString){var token;var hours;var minutes;token=parseTokenHH.exec(timeString);if(token){hours=parseFloat(token[1].replace(",","."));return hours%24*MILLISECONDS_IN_HOUR}token=parseTokenHHMM.exec(timeString);if(token){hours=parseInt(token[1],10);minutes=parseFloat(token[2].replace(",","."));return hours%24*MILLISECONDS_IN_HOUR+minutes*MILLISECONDS_IN_MINUTE}token=parseTokenHHMMSS.exec(timeString);if(token){hours=parseInt(token[1],10);minutes=parseInt(token[2],10);var seconds=parseFloat(token[3].replace(",","."));return hours%24*MILLISECONDS_IN_HOUR+minutes*MILLISECONDS_IN_MINUTE+seconds*1e3}return null}function parseTimezone(timezoneString){var token;var absoluteOffset;token=parseTokenTimezoneZ.exec(timezoneString);if(token){return 0}token=parseTokenTimezoneHH.exec(timezoneString);if(token){absoluteOffset=parseInt(token[2],10)*60;return token[1]==="+"?-absoluteOffset:absoluteOffset}token=parseTokenTimezoneHHMM.exec(timezoneString);if(token){absoluteOffset=parseInt(token[2],10)*60+parseInt(token[3],10);return token[1]==="+"?-absoluteOffset:absoluteOffset}return 0}function dayOfISOYear(isoYear,week,day){week=week||0;day=day||0;var date=new Date(0);date.setUTCFullYear(isoYear,0,4);var fourthOfJanuaryDay=date.getUTCDay()||7;var diff=week*7+day+1-fourthOfJanuaryDay;date.setUTCDate(date.getUTCDate()+diff);return date}module.exports=parse},function(module,exports){function isDate(argument){return argument instanceof Date}module.exports=isDate},function(module,exports,__webpack_require__){var addMilliseconds=__webpack_require__(5);var MILLISECONDS_IN_HOUR=36e5;function addHours(dirtyDate,dirtyAmount){var amount=Number(dirtyAmount);return addMilliseconds(dirtyDate,amount*MILLISECONDS_IN_HOUR)}module.exports=addHours},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function addMilliseconds(dirtyDate,dirtyAmount){var timestamp=parse(dirtyDate).getTime();var amount=Number(dirtyAmount);return new Date(timestamp+amount)}module.exports=addMilliseconds},function(module,exports,__webpack_require__){var getISOYear=__webpack_require__(7);var setISOYear=__webpack_require__(10);function addISOYears(dirtyDate,dirtyAmount){var amount=Number(dirtyAmount);return setISOYear(dirtyDate,getISOYear(dirtyDate)+amount)}module.exports=addISOYears},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);var startOfISOWeek=__webpack_require__(8);function getISOYear(dirtyDate){var date=parse(dirtyDate);var year=date.getFullYear();var fourthOfJanuaryOfNextYear=new Date(0);fourthOfJanuaryOfNextYear.setFullYear(year+1,0,4);fourthOfJanuaryOfNextYear.setHours(0,0,0,0);var startOfNextYear=startOfISOWeek(fourthOfJanuaryOfNextYear);var fourthOfJanuaryOfThisYear=new Date(0);fourthOfJanuaryOfThisYear.setFullYear(year,0,4);fourthOfJanuaryOfThisYear.setHours(0,0,0,0);var startOfThisYear=startOfISOWeek(fourthOfJanuaryOfThisYear);if(date.getTime()>=startOfNextYear.getTime()){return year+1}else if(date.getTime()>=startOfThisYear.getTime()){return year}else{return year-1}}module.exports=getISOYear},function(module,exports,__webpack_require__){var startOfWeek=__webpack_require__(9);function startOfISOWeek(dirtyDate){return startOfWeek(dirtyDate,{weekStartsOn:1})}module.exports=startOfISOWeek},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function startOfWeek(dirtyDate,dirtyOptions){var weekStartsOn=dirtyOptions?Number(dirtyOptions.weekStartsOn)||0:0;var date=parse(dirtyDate);var day=date.getDay();var diff=(dayinitialEndTime||comparedStartTime>comparedEndTime){throw new Error("The start of the range cannot be after the end of the range")}return initialStartTimetimeRight){return 1}else{return 0}}module.exports=compareAsc},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function compareDesc(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var timeLeft=dateLeft.getTime();var dateRight=parse(dirtyDateRight);var timeRight=dateRight.getTime();if(timeLeft>timeRight){return-1}else if(timeLeft0?Math.floor(diff):Math.ceil(diff)}module.exports=differenceInHours},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function differenceInMilliseconds(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);return dateLeft.getTime()-dateRight.getTime()}module.exports=differenceInMilliseconds},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);var differenceInCalendarISOYears=__webpack_require__(27);var compareAsc=__webpack_require__(24);var subISOYears=__webpack_require__(37);function differenceInISOYears(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);var sign=compareAsc(dateLeft,dateRight);var difference=Math.abs(differenceInCalendarISOYears(dateLeft,dateRight));dateLeft=subISOYears(dateLeft,sign*difference);var isLastISOYearNotFull=compareAsc(dateLeft,dateRight)===-sign;return sign*(difference-isLastISOYearNotFull)}module.exports=differenceInISOYears},function(module,exports,__webpack_require__){var addISOYears=__webpack_require__(6);function subISOYears(dirtyDate,dirtyAmount){var amount=Number(dirtyAmount);return addISOYears(dirtyDate,-amount)}module.exports=subISOYears},function(module,exports,__webpack_require__){var differenceInMilliseconds=__webpack_require__(35);var MILLISECONDS_IN_MINUTE=6e4;function differenceInMinutes(dirtyDateLeft,dirtyDateRight){var diff=differenceInMilliseconds(dirtyDateLeft,dirtyDateRight)/MILLISECONDS_IN_MINUTE;return diff>0?Math.floor(diff):Math.ceil(diff)}module.exports=differenceInMinutes},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);var differenceInCalendarMonths=__webpack_require__(28);var compareAsc=__webpack_require__(24);function differenceInMonths(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);var sign=compareAsc(dateLeft,dateRight);var difference=Math.abs(differenceInCalendarMonths(dateLeft,dateRight));dateLeft.setMonth(dateLeft.getMonth()-sign*difference);var isLastMonthNotFull=compareAsc(dateLeft,dateRight)===-sign;return sign*(difference-isLastMonthNotFull)}module.exports=differenceInMonths},function(module,exports,__webpack_require__){var differenceInMonths=__webpack_require__(39);function differenceInQuarters(dirtyDateLeft,dirtyDateRight){var diff=differenceInMonths(dirtyDateLeft,dirtyDateRight)/3;return diff>0?Math.floor(diff):Math.ceil(diff)}module.exports=differenceInQuarters},function(module,exports,__webpack_require__){var differenceInMilliseconds=__webpack_require__(35);function differenceInSeconds(dirtyDateLeft,dirtyDateRight){var diff=differenceInMilliseconds(dirtyDateLeft,dirtyDateRight)/1e3;return diff>0?Math.floor(diff):Math.ceil(diff)}module.exports=differenceInSeconds},function(module,exports,__webpack_require__){var differenceInDays=__webpack_require__(33);function differenceInWeeks(dirtyDateLeft,dirtyDateRight){var diff=differenceInDays(dirtyDateLeft,dirtyDateRight)/7;return diff>0?Math.floor(diff):Math.ceil(diff)}module.exports=differenceInWeeks},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);var differenceInCalendarYears=__webpack_require__(32);var compareAsc=__webpack_require__(24);function differenceInYears(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);var sign=compareAsc(dateLeft,dateRight);var difference=Math.abs(differenceInCalendarYears(dateLeft,dateRight));dateLeft.setFullYear(dateLeft.getFullYear()-sign*difference);var isLastYearNotFull=compareAsc(dateLeft,dateRight)===-sign;return sign*(difference-isLastYearNotFull)}module.exports=differenceInYears},function(module,exports,__webpack_require__){var compareDesc=__webpack_require__(25);var parse=__webpack_require__(2);var differenceInSeconds=__webpack_require__(41);var differenceInMonths=__webpack_require__(39);var enLocale=__webpack_require__(45);var MINUTES_IN_DAY=1440;var MINUTES_IN_ALMOST_TWO_DAYS=2520;var MINUTES_IN_MONTH=43200;var MINUTES_IN_TWO_MONTHS=86400;function distanceInWords(dirtyDateToCompare,dirtyDate,dirtyOptions){var options=dirtyOptions||{};var comparison=compareDesc(dirtyDateToCompare,dirtyDate);var locale=options.locale;var localize=enLocale.distanceInWords.localize;if(locale&&locale.distanceInWords&&locale.distanceInWords.localize){localize=locale.distanceInWords.localize}var localizeOptions={addSuffix:Boolean(options.addSuffix),comparison:comparison};var dateLeft,dateRight;if(comparison>0){dateLeft=parse(dirtyDateToCompare);dateRight=parse(dirtyDate)}else{dateLeft=parse(dirtyDate);dateRight=parse(dirtyDateToCompare)}var seconds=differenceInSeconds(dateRight,dateLeft);var offset=dateRight.getTimezoneOffset()-dateLeft.getTimezoneOffset();var minutes=Math.round(seconds/60)-offset;var months;if(minutes<2){if(options.includeSeconds){if(seconds<5){return localize("lessThanXSeconds",5,localizeOptions)}else if(seconds<10){return localize("lessThanXSeconds",10,localizeOptions)}else if(seconds<20){return localize("lessThanXSeconds",20,localizeOptions)}else if(seconds<40){return localize("halfAMinute",null,localizeOptions)}else if(seconds<60){return localize("lessThanXMinutes",1,localizeOptions)}else{return localize("xMinutes",1,localizeOptions)}}else{if(minutes===0){return localize("lessThanXMinutes",1,localizeOptions)}else{return localize("xMinutes",minutes,localizeOptions)}}}else if(minutes<45){return localize("xMinutes",minutes,localizeOptions)}else if(minutes<90){return localize("aboutXHours",1,localizeOptions)}else if(minutes0){return"in "+result}else{return result+" ago"}}return result}return{localize:localize}}module.exports=buildDistanceInWordsLocale},function(module,exports,__webpack_require__){var buildFormattingTokensRegExp=__webpack_require__(48);function buildFormatLocale(){var months3char=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];var monthsFull=["January","February","March","April","May","June","July","August","September","October","November","December"];var weekdays2char=["Su","Mo","Tu","We","Th","Fr","Sa"];var weekdays3char=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];var weekdaysFull=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var meridiemUppercase=["AM","PM"];var meridiemLowercase=["am","pm"];var meridiemFull=["a.m.","p.m."];var formatters={MMM:function(date){return months3char[date.getMonth()]},MMMM:function(date){return monthsFull[date.getMonth()]},dd:function(date){return weekdays2char[date.getDay()]},ddd:function(date){return weekdays3char[date.getDay()]},dddd:function(date){return weekdaysFull[date.getDay()]},A:function(date){return date.getHours()/12>=1?meridiemUppercase[1]:meridiemUppercase[0]},a:function(date){return date.getHours()/12>=1?meridiemLowercase[1]:meridiemLowercase[0]},aa:function(date){return date.getHours()/12>=1?meridiemFull[1]:meridiemFull[0]}};var ordinalFormatters=["M","D","DDD","d","Q","W"];ordinalFormatters.forEach(function(formatterToken){formatters[formatterToken+"o"]=function(date,formatters){return ordinal(formatters[formatterToken](date))}});return{formatters:formatters,formattingTokensRegExp:buildFormattingTokensRegExp(formatters)}}function ordinal(number){var rem100=number%100;if(rem100>20||rem100<10){switch(rem100%10){case 1:return number+"st";case 2:return number+"nd";case 3:return number+"rd"}}return number+"th"}module.exports=buildFormatLocale},function(module,exports){var commonFormatterKeys=["M","MM","Q","D","DD","DDD","DDDD","d","E","W","WW","YY","YYYY","GG","GGGG","H","HH","h","hh","m","mm","s","ss","S","SS","SSS","Z","ZZ","X","x"];function buildFormattingTokensRegExp(formatters){var formatterKeys=[];for(var key in formatters){if(formatters.hasOwnProperty(key)){formatterKeys.push(key)}}var formattingTokens=commonFormatterKeys.concat(formatterKeys).sort().reverse();var formattingTokensRegExp=new RegExp("(\\[[^\\[]*\\])|(\\\\)?"+"("+formattingTokens.join("|")+"|.)","g");return formattingTokensRegExp}module.exports=buildFormattingTokensRegExp},function(module,exports,__webpack_require__){var compareDesc=__webpack_require__(25);var parse=__webpack_require__(2);var differenceInSeconds=__webpack_require__(41);var enLocale=__webpack_require__(45);var MINUTES_IN_DAY=1440;var MINUTES_IN_MONTH=43200;var MINUTES_IN_YEAR=525600;function distanceInWordsStrict(dirtyDateToCompare,dirtyDate,dirtyOptions){var options=dirtyOptions||{};var comparison=compareDesc(dirtyDateToCompare,dirtyDate);var locale=options.locale;var localize=enLocale.distanceInWords.localize;if(locale&&locale.distanceInWords&&locale.distanceInWords.localize){localize=locale.distanceInWords.localize}var localizeOptions={addSuffix:Boolean(options.addSuffix),comparison:comparison};var dateLeft,dateRight;if(comparison>0){dateLeft=parse(dirtyDateToCompare);dateRight=parse(dirtyDate)}else{dateLeft=parse(dirtyDate);dateRight=parse(dirtyDateToCompare)}var unit;var mathPartial=Math[options.partialMethod?String(options.partialMethod):"floor"];var seconds=differenceInSeconds(dateRight,dateLeft);var offset=dateRight.getTimezoneOffset()-dateLeft.getTimezoneOffset();var minutes=mathPartial(seconds/60)-offset;var hours,days,months,years;if(options.unit){unit=String(options.unit)}else{if(minutes<1){unit="s"}else if(minutes<60){unit="m"}else if(minutesendTime){throw new Error("The first date cannot be after the second date")}var dates=[];var currentDate=startDate;currentDate.setHours(0,0,0,0);while(currentDate.getTime()<=endTime){dates.push(parse(currentDate));currentDate.setDate(currentDate.getDate()+1)}return dates}module.exports=eachDay},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function endOfDay(dirtyDate){var date=parse(dirtyDate);date.setHours(23,59,59,999);return date}module.exports=endOfDay},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function endOfHour(dirtyDate){var date=parse(dirtyDate);date.setMinutes(59,59,999);return date}module.exports=endOfHour},function(module,exports,__webpack_require__){var endOfWeek=__webpack_require__(55);function endOfISOWeek(dirtyDate){return endOfWeek(dirtyDate,{weekStartsOn:1})}module.exports=endOfISOWeek},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function endOfWeek(dirtyDate,dirtyOptions){var weekStartsOn=dirtyOptions?Number(dirtyOptions.weekStartsOn)||0:0;var date=parse(dirtyDate);var day=date.getDay();var diff=(day12){return hours%12}else{return hours}},hh:function(date){return addLeadingZeros(formatters["h"](date),2)},m:function(date){return date.getMinutes()},mm:function(date){return addLeadingZeros(date.getMinutes(),2)},s:function(date){return date.getSeconds()},ss:function(date){return addLeadingZeros(date.getSeconds(),2)},S:function(date){return Math.floor(date.getMilliseconds()/100)},SS:function(date){return addLeadingZeros(Math.floor(date.getMilliseconds()/10),2)},SSS:function(date){return addLeadingZeros(date.getMilliseconds(),3)},Z:function(date){return formatTimezone(date.getTimezoneOffset(),":")},ZZ:function(date){return formatTimezone(date.getTimezoneOffset())},X:function(date){return Math.floor(date.getTime()/1e3)},x:function(date){return date.getTime()}};function buildFormatFn(formatStr,localeFormatters,formattingTokensRegExp){var array=formatStr.match(formattingTokensRegExp);var length=array.length;var i;var formatter;for(i=0;i0?"-":"+";var absOffset=Math.abs(offset);var hours=Math.floor(absOffset/60);var minutes=absOffset%60;return sign+addLeadingZeros(hours,2)+delimeter+addLeadingZeros(minutes,2)}function addLeadingZeros(number,targetLength){var output=Math.abs(number).toString();while(output.lengthinitialEndTime||comparedStartTime>comparedEndTime){throw new Error("The start of the range cannot be after the end of the range")}var isOverlapping=initialStartTimeinitialEndTime?initialEndTime:comparedEndTime;var differenceInMs=overlapEndDate-overlapStartDate;return Math.ceil(differenceInMs/MILLISECONDS_IN_DAY)}module.exports=getOverlappingDaysInRanges},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function getSeconds(dirtyDate){var date=parse(dirtyDate);var seconds=date.getSeconds();return seconds}module.exports=getSeconds},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function getTime(dirtyDate){var date=parse(dirtyDate);var timestamp=date.getTime();return timestamp}module.exports=getTime},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function getYear(dirtyDate){var date=parse(dirtyDate);var year=date.getFullYear();return year}module.exports=getYear},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isAfter(dirtyDate,dirtyDateToCompare){var date=parse(dirtyDate);var dateToCompare=parse(dirtyDateToCompare);return date.getTime()>dateToCompare.getTime()}module.exports=isAfter},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isBefore(dirtyDate,dirtyDateToCompare){var date=parse(dirtyDate);var dateToCompare=parse(dirtyDateToCompare);return date.getTime()(new Date).getTime()}module.exports=isFuture},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);var endOfDay=__webpack_require__(52);var endOfMonth=__webpack_require__(58);function isLastDayOfMonth(dirtyDate){var date=parse(dirtyDate);return endOfDay(date).getTime()===endOfMonth(date).getTime()}module.exports=isLastDayOfMonth},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isMonday(dirtyDate){return parse(dirtyDate).getDay()===1}module.exports=isMonday},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isPast(dirtyDate){return parse(dirtyDate).getTime()<(new Date).getTime()}module.exports=isPast},function(module,exports,__webpack_require__){var startOfDay=__webpack_require__(13);function isSameDay(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfDay=startOfDay(dirtyDateLeft);var dateRightStartOfDay=startOfDay(dirtyDateRight);return dateLeftStartOfDay.getTime()===dateRightStartOfDay.getTime()}module.exports=isSameDay},function(module,exports,__webpack_require__){var startOfHour=__webpack_require__(95);function isSameHour(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfHour=startOfHour(dirtyDateLeft);var dateRightStartOfHour=startOfHour(dirtyDateRight);return dateLeftStartOfHour.getTime()===dateRightStartOfHour.getTime()}module.exports=isSameHour},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function startOfHour(dirtyDate){var date=parse(dirtyDate);date.setMinutes(0,0,0);return date}module.exports=startOfHour},function(module,exports,__webpack_require__){var isSameWeek=__webpack_require__(97);function isSameISOWeek(dirtyDateLeft,dirtyDateRight){return isSameWeek(dirtyDateLeft,dirtyDateRight,{weekStartsOn:1})}module.exports=isSameISOWeek},function(module,exports,__webpack_require__){var startOfWeek=__webpack_require__(9);function isSameWeek(dirtyDateLeft,dirtyDateRight,dirtyOptions){var dateLeftStartOfWeek=startOfWeek(dirtyDateLeft,dirtyOptions);var dateRightStartOfWeek=startOfWeek(dirtyDateRight,dirtyOptions);return dateLeftStartOfWeek.getTime()===dateRightStartOfWeek.getTime()}module.exports=isSameWeek},function(module,exports,__webpack_require__){var startOfISOYear=__webpack_require__(11);function isSameISOYear(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfYear=startOfISOYear(dirtyDateLeft);var dateRightStartOfYear=startOfISOYear(dirtyDateRight);return dateLeftStartOfYear.getTime()===dateRightStartOfYear.getTime()}module.exports=isSameISOYear},function(module,exports,__webpack_require__){var startOfMinute=__webpack_require__(100);function isSameMinute(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfMinute=startOfMinute(dirtyDateLeft);var dateRightStartOfMinute=startOfMinute(dirtyDateRight);return dateLeftStartOfMinute.getTime()===dateRightStartOfMinute.getTime()}module.exports=isSameMinute},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function startOfMinute(dirtyDate){var date=parse(dirtyDate);date.setSeconds(0,0);return date}module.exports=startOfMinute},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isSameMonth(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);return dateLeft.getFullYear()===dateRight.getFullYear()&&dateLeft.getMonth()===dateRight.getMonth()}module.exports=isSameMonth},function(module,exports,__webpack_require__){var startOfQuarter=__webpack_require__(103);function isSameQuarter(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfQuarter=startOfQuarter(dirtyDateLeft);var dateRightStartOfQuarter=startOfQuarter(dirtyDateRight);return dateLeftStartOfQuarter.getTime()===dateRightStartOfQuarter.getTime()}module.exports=isSameQuarter},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function startOfQuarter(dirtyDate){var date=parse(dirtyDate);var currentMonth=date.getMonth();var month=currentMonth-currentMonth%3;date.setMonth(month,1);date.setHours(0,0,0,0);return date}module.exports=startOfQuarter},function(module,exports,__webpack_require__){var startOfSecond=__webpack_require__(105);function isSameSecond(dirtyDateLeft,dirtyDateRight){var dateLeftStartOfSecond=startOfSecond(dirtyDateLeft);var dateRightStartOfSecond=startOfSecond(dirtyDateRight);return dateLeftStartOfSecond.getTime()===dateRightStartOfSecond.getTime()}module.exports=isSameSecond},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function startOfSecond(dirtyDate){var date=parse(dirtyDate);date.setMilliseconds(0);return date}module.exports=startOfSecond},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isSameYear(dirtyDateLeft,dirtyDateRight){var dateLeft=parse(dirtyDateLeft);var dateRight=parse(dirtyDateRight);return dateLeft.getFullYear()===dateRight.getFullYear()}module.exports=isSameYear},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isSaturday(dirtyDate){return parse(dirtyDate).getDay()===6}module.exports=isSaturday},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isSunday(dirtyDate){return parse(dirtyDate).getDay()===0}module.exports=isSunday},function(module,exports,__webpack_require__){var isSameHour=__webpack_require__(94);function isThisHour(dirtyDate){return isSameHour(new Date,dirtyDate)}module.exports=isThisHour},function(module,exports,__webpack_require__){var isSameISOWeek=__webpack_require__(96);function isThisISOWeek(dirtyDate){return isSameISOWeek(new Date,dirtyDate)}module.exports=isThisISOWeek},function(module,exports,__webpack_require__){var isSameISOYear=__webpack_require__(98);function isThisISOYear(dirtyDate){return isSameISOYear(new Date,dirtyDate)}module.exports=isThisISOYear},function(module,exports,__webpack_require__){var isSameMinute=__webpack_require__(99);function isThisMinute(dirtyDate){return isSameMinute(new Date,dirtyDate)}module.exports=isThisMinute},function(module,exports,__webpack_require__){var isSameMonth=__webpack_require__(101);function isThisMonth(dirtyDate){return isSameMonth(new Date,dirtyDate)}module.exports=isThisMonth},function(module,exports,__webpack_require__){var isSameQuarter=__webpack_require__(102);function isThisQuarter(dirtyDate){return isSameQuarter(new Date,dirtyDate)}module.exports=isThisQuarter},function(module,exports,__webpack_require__){var isSameSecond=__webpack_require__(104);function isThisSecond(dirtyDate){return isSameSecond(new Date,dirtyDate)}module.exports=isThisSecond},function(module,exports,__webpack_require__){var isSameWeek=__webpack_require__(97);function isThisWeek(dirtyDate,dirtyOptions){return isSameWeek(new Date,dirtyDate,dirtyOptions)}module.exports=isThisWeek},function(module,exports,__webpack_require__){var isSameYear=__webpack_require__(106);function isThisYear(dirtyDate){return isSameYear(new Date,dirtyDate)}module.exports=isThisYear},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isThursday(dirtyDate){return parse(dirtyDate).getDay()===4}module.exports=isThursday},function(module,exports,__webpack_require__){var startOfDay=__webpack_require__(13);function isToday(dirtyDate){return startOfDay(dirtyDate).getTime()===startOfDay(new Date).getTime()}module.exports=isToday},function(module,exports,__webpack_require__){var startOfDay=__webpack_require__(13);function isTomorrow(dirtyDate){var tomorrow=new Date;tomorrow.setDate(tomorrow.getDate()+1);return startOfDay(dirtyDate).getTime()===startOfDay(tomorrow).getTime()}module.exports=isTomorrow},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isTuesday(dirtyDate){return parse(dirtyDate).getDay()===2}module.exports=isTuesday},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isWednesday(dirtyDate){return parse(dirtyDate).getDay()===3}module.exports=isWednesday},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isWeekend(dirtyDate){var date=parse(dirtyDate);var day=date.getDay();return day===0||day===6}module.exports=isWeekend},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function isWithinRange(dirtyDate,dirtyStartDate,dirtyEndDate){var time=parse(dirtyDate).getTime();var startTime=parse(dirtyStartDate).getTime();var endTime=parse(dirtyEndDate).getTime();if(startTime>endTime){throw new Error("The start of the range cannot be after the end of the range")}return time>=startTime&&time<=endTime}module.exports=isWithinRange},function(module,exports,__webpack_require__){var startOfDay=__webpack_require__(13);function isYesterday(dirtyDate){var yesterday=new Date;yesterday.setDate(yesterday.getDate()-1);return startOfDay(dirtyDate).getTime()===startOfDay(yesterday).getTime()}module.exports=isYesterday},function(module,exports,__webpack_require__){var lastDayOfWeek=__webpack_require__(127);function lastDayOfISOWeek(dirtyDate){return lastDayOfWeek(dirtyDate,{weekStartsOn:1})}module.exports=lastDayOfISOWeek},function(module,exports,__webpack_require__){var parse=__webpack_require__(2);function lastDayOfWeek(dirtyDate,dirtyOptions){var weekStartsOn=dirtyOptions?Number(dirtyOptions.weekStartsOn)||0:0;var date=parse(dirtyDate);var day=date.getDay();var diff=(day").appendTo('#marquee-now');
+ } else {
+ $span = $('#marquee-now>span#' + location.woeid);
+ }
+
+ // display the current info
+ $span.text(location.name + ': ' + Math.round(parseInt(location.data.main.temp)) + ' ' + location.data.weather[0].description.toLowerCase());
+
+
+ // set the expiration date/time
+ location.xdate = dateFns.addMinutes(location.data.lastBuildDate, location.data.ttl);
+
+ });
+
+ }
+
+
+}
+var groupDataManager = new GroupDataManager;
diff --git a/webroot/location.js b/webroot/location.js
new file mode 100644
index 0000000..804b80f
--- /dev/null
+++ b/webroot/location.js
@@ -0,0 +1,179 @@
+function Location() { // onReady, onRefresh, onAllComplete
+
+ var that = this,
+ $this = $(this),
+ _forecastmgr,
+ _observations=[];
+
+ this.temperature = function() {
+ if (_observations[1]!=null && _observations[1].temperature.value) {
+ return C2F(_observations[1].temperature.value);
+ } else {
+ return Math.round( _observations[0].current.temp );
+ }
+ }
+
+ this.observations = function(i) {
+ return _observations[i];
+ };
+
+ this.forecasts=function(type){return _forecastmgr.forecast(type)};
+
+ this.init = function(searchString){
+ checkRefresh(searchString);
+ };
+
+ this.initForecasts = function() {
+ // start the forecast data pull
+ if (_observations[0] != undefined){
+ _forecastmgr = new ForecastManager(_observations[0].lat, _observations[0].lon, function() {
+ $this.trigger('ready');
+ });
+ }
+ };
+
+ this.initNWSObservations = function(){
+ stationObservations();
+ };
+
+
+ // check to see if data needs to be refreshed
+ function checkRefresh(location) {
+
+ // check the expiration
+ if ( _observations[0]!=undefined && dateFns.isFuture( _observations[0].xdate ) ) {
+ setTimeout(checkRefresh, getRandom(5000, 10000));
+ return;
+ }
+
+ // woeid is the id for the location to pull data for
+ console.log(location);
+ if (location != undefined) {
+ var loclat = location.split(",")[0]
+ var loclong = location.split(",")[1]
+ var url = 'https://api.openweathermap.org/data/2.5/onecall?lat=' + loclat + '&lon=' + loclong + '&appid=putkeyhere&units=imperial'
+
+
+ // ajax the latest observation
+ $.getJSON(url, function(data) {
+ _observations[0] = json = data;
+
+ $this.trigger('refresh');
+
+ // the following block only runs on init
+ if (that.woeid===undefined) {
+
+ that.woeid = loclat
+
+ that.lat = data.lat
+ that.long = data.lon;
+
+ //that.city = data.query.results.channel.location.city;
+
+ $this.trigger('init');
+
+ }
+ // set the expiration date/time
+ _observations[0].xdate = dateFns.addMinutes(json.lastBuildDate, json.ttl);
+
+ setTimeout(checkRefresh, getRandom(5000, 10000));
+
+ });
+ $.getJSON('http://api.openweathermap.org/geo/1.0/reverse?lat=' + loclat + '&lon=' + loclong + '&limit=1&appid=0cb279a98124446dd16dba02fbfb60ee', function(sloc) {
+ that.city = (sloc[0].name);
+ });
+
+}
+
+}
+
+ // pull observations from the location observation station
+ function stationObservations() {
+
+ var url = that.stationUrl + '/observations/current';
+
+ // check the expiration
+ if ( _observations[1]!=undefined && dateFns.isFuture( _observations[1].xdate ) ) {
+ setTimeout(checkRefresh, getRandom(5000, 10000));
+ return;
+ }
+
+ // ajax the current conditions
+ $.getJSON(url, function(data) {
+ _observations[1] = data.properties;
+
+ // set the expiration date/time
+ _observations[1].xdate = dateFns.addMinutes(data.properties.timestamp, 60);
+ setTimeout(stationObservations, getRandom(5000, 10000));
+
+ });
+ }
+
+
+
+
+function ForecastManager (latitude, longitude, readyCallback) {
+ var _forecasts = {},
+ keys =['daily','hourly'],
+ key,
+ readycount = 0;
+
+ for(key of keys) {
+ _forecasts[key] = new Forecast(key, latitude, longitude, count);
+ }
+
+
+ function count() {
+ // count up completed forecast pulls
+ readycount++;
+ if (readycount===keys.length) {
+ readyCallback();
+ }
+ }
+
+ this.forecast = function(type) {
+ try{
+ return _forecasts[type].data;
+ } catch(err){}
+ }
+
+}
+
+function Forecast(type, lat, lon, readyCallback) {
+
+ var that = this,
+ url = 'https://api.weather.gov/points/' + lat + ',' + lon + "/forecast/" + (type==='hourly' ? type : '');
+
+ this.data = {};
+
+ checkRefresh();
+
+ function checkRefresh() {
+
+ // check the expiration
+ if ( that.data!={} && dateFns.isFuture( that.data.xdate ) ) {
+ setTimeout(checkRefresh, getRandom(5000, 10000));
+ return;
+ }
+
+ // ajax the forecast
+ $.getJSON(url, function(data) {
+
+ that.data = data.properties.periods;
+
+ // trigger ready callback on first data pull
+ if (readyCallback){
+ readyCallback();
+ }
+
+ // set the expiration date/time
+ that.data.xdate = dateFns.addMinutes(data.properties.updated, 60);
+ that.data.xdate = dateFns.addMinutes(new Date(), 5);
+ setTimeout(checkRefresh, getRandom(5000, 10000));
+
+ });
+
+ }
+
+}
+}
diff --git a/webroot/loops.js b/webroot/loops.js
new file mode 100644
index 0000000..79bd5f9
--- /dev/null
+++ b/webroot/loops.js
@@ -0,0 +1,522 @@
+
+function Loops(bindDataManager) {
+ var //dataManager,
+ obsData,
+ foreDataDaily,
+ foreDataHourly;
+
+ obsData = bindDataManager.observations;
+ foreDataDaily = bindDataManager.forecasts('daily');
+ foreDataHourly = bindDataManager.forecasts('hourly');
+
+
+ // init the display loops
+ displayAtmospheric(0);
+ displayForecast(0);
+
+ function displayAtmospheric(idx) {
+
+ var displays = {
+
+ conditions() {
+ return (getCC(obsData(0).current.weather[0].id + obsData(0).current.weather[0].icon, obsData(0).current.wind_speed)).toLowerCase();
+ },
+
+ wind(){ return 'wind ' + degToCompass(obsData(0).current.wind_deg) + ' ' + Math.round(parseInt(obsData(0).current.wind_speed)); },
+
+ gusts(){
+ if ( obsData(1)!=undefined ) {
+ return obsData(1).windGust.value!=null ? 'gusts ' + mps2mph( obsData(1).windGust.value ) : '';
+ }
+ },
+
+ humidity(){ return 'humidity ' + obsData(0).current.humidity + '%'; },
+
+ dewpoint(){ return 'dew point ' + dewPoint(obsData(0).current.temp, obsData(0).current.humidity ) + '°'; },
+
+ heatindex_windchill(){
+ var windchill = 35.74 + (0.6215 * parseInt(obsData(0).current.temp)) + (0.4275 * parseInt(obsData(0).current.temp) - 35.75) * parseInt(obsData(0).current.wind_speed) ^ 0.16;
+ if (parseInt(obsData(0).current.temp)<80 && windchill < parseInt(obsData(0).current.temp)) {
+
+ return 'wind chill ' + windchill + '°';
+ } else if (parseInt(obsData(0).current.temp)>=80 && parseInt(obsData(0).current.humidity)>=40 ){
+ return 'heat index ' + heatIndex(obsData(0).current.temp, obsData(0).current.humidity) + '°';
+ }
+ else return '';
+ },
+
+ pressure(){ return 'pressure ' + (obsData(0).current.pressure*0.0295301).toFixed(2) },
+//+ ['S','R','F'][obsData(0).current.rising];
+ visibility() { return 'visibility ' + (parseInt(obsData(0).current.visibility) / 1000) + ' mile' + (obsData(0).current.visibility != 1 ? 's' : ''); },
+
+ uvindex() { return 'UV index ' + obsData(0).current.uvi; },
+
+ },
+ keys = Object.keys(displays),
+ text = displays[ keys[idx] ]();
+
+ // increment the pointer
+ idx = (++idx===keys.length ? 0 : idx);
+
+ if (text) {
+ $('#current-info').html(text);
+ setTimeout(function(){ displayAtmospheric(idx) }, 6000); // 6 second increment loop
+ } else {
+ // nothing to display - skip to the next one
+ setTimeout(function(){ displayAtmospheric(idx) }, 0);
+ }
+
+ } // end function
+
+
+ function displayForecast(idx) {
+
+ var displays = {
+
+ text1() {
+ $('#forecast-title').text( possessiveForecast(foreDataDaily[0].name) );
+ resizeText(foreDataDaily[0].detailedForecast);
+ },
+ text2() {
+ $('#forecast-title').text( possessiveForecast(foreDataDaily[1].name) );
+ resizeText(foreDataDaily[1].detailedForecast);
+ },
+
+ fiveday() {
+ var newtile, weekend, icons,
+ startidx = (foreDataDaily[0].name==='Tonight' ? 1 : 2),
+ days = ['SUN','MON','TUE','WED','THU','FRI','SAT'];
+
+ $('#forecast-title').text("5 DAY FORECAST");
+ $('#forecast-tiles').empty();
+
+ for (var i=startidx; i<=10; i+=2 ) {
+
+ weekend = ( dateFns.isWeekend(foreDataDaily[i].startTime) ? ' weekend' : '');
+ newtile = $("");
+
+ $("") .appendTo(newtile) .text(days[ dateFns.getDay(foreDataDaily[i].startTime) ]);
+
+ icons = mapNWSicons(foreDataDaily[i].icon);
+ for (x=icons.length-1; x>=0; x--){
+ $("
") .appendTo(newtile) .attr('src', icons[x]);
+ }
+
+ $("") .appendTo(newtile) .text(foreDataDaily[i].temperature);
+ $("") .appendTo(newtile) .text(foreDataDaily[i+1].temperature);
+
+ $('#forecast-tiles').append(newtile);
+ }
+
+ $('#forecast-tiles').css('display','flex');
+ },
+
+ hourly() {
+ var newtile, icons, sizer, highbar,
+ indexes = calcHourlyReport(foreDataHourly),
+ data, label, temps=[];
+
+ $('#forecast-title').text( buildHourlyHeaderTitle(foreDataHourly[indexes[0]].startTime) );
+ $('#forecast-tiles').empty();
+
+ for (var i of indexes) {
+ data = foreDataHourly[i];
+
+ newtile = $("");
+ sizer = $("").appendTo(newtile);
+
+ icons = mapNWSicons(data.icon);
+ for (var x=icons.length-1; x>=0; x--){
+ $("
") .appendTo(sizer) .attr('src', icons[x]);
+ }
+
+ $("") .appendTo(newtile) .text(buildHourlyTimeTitle(data.startTime));
+
+ highbar = $("") .appendTo(sizer);
+
+ $("") .appendTo(highbar) .text(data.temperature);
+ temps.push(data.temperature);
+
+ $("") .appendTo(highbar);
+
+ $('#forecast-tiles').append(newtile);
+ }
+
+ $('#forecast-tiles').css('display','flex');
+
+ // animate grow and show temp
+ var min = Math.min(...temps), // 54
+ max = Math.max(...temps), // 73
+ range = (max-min),
+ prange = (95-78), // percent range for bar height
+ temp, value;
+ $('.forecast-tile').each(function(){
+ temp = $(this).find('.high').first().text();
+ value = ((temp-min)/range) * prange + 78; // find percentage of range and translate to percent and add that to the starting css % height number
+ $(this).find('.hourly-high').animate({height:value+"%"}, 1500,function(){
+ $(this).find('.high').fadeTo('slow', 1);
+ });
+ })
+ },
+
+ dummy(){}
+
+ },
+ keys = Object.keys(displays);
+
+ displays[ keys[idx] ]();
+
+ // increment the pointer
+ idx = (++idx===keys.length ? 0 : idx);
+
+ setTimeout(function(){ displayForecast(idx) }, 15000); // 15 second increment loop
+
+ }
+
+ function resizeText(text){
+ var s = 38,
+ $test = $('') .appendTo('#forecast-text') .css('font-size', s + 'px') .html(text);
+ $test.width($('#forecast-text').width() );
+ //setTimeout(function() {
+ while ($test.outerHeight(true) >= ($('#forecast-text').height()) ) {
+ s -= 1;
+ $test.css('font-size', s + 'px');
+ }
+ $('#forecast-text div') .text(text) .css('font-size', s + 'px');
+ $test.remove();
+ $('#forecast-tiles').hide();
+ //},100); // delay is a workaround for Interstate font not updating display
+ }
+
+ function possessiveForecast(text){
+ return text + (text.toUpperCase() != 'OVERNIGHT' ? "'S" : '') + ' FORECAST';
+ }
+
+
+} // end Loops class
+
+
+function buildHourlyHeaderTitle(time) {
+ var today = new Date(),
+ tomorrow = dateFns.addDays(today, 1),
+ sforecast = "'s Forecast";
+
+ // title based on the first hour reported
+ switch (dateFns.getHours(time)) {
+
+ case 6: // 6 - Nextday's Forecast / Today's Forecast
+ // if 6am today
+ if (dateFns.isToday(time)) {
+ return dateFns.format(today, 'dddd') + sforecast;
+ }
+ case 0: // 0 - Nextday's Forecast
+ return dateFns.format(tomorrow, 'dddd') + sforecast;
+
+ case 12:
+ return 'This Afternoon';
+
+ case 15:
+ return "Today's Forecast";
+
+ case 17:
+ return "Tonight's Forecast";
+
+ case 20:
+ return dateFns.format(today, 'ddd') + ' Night/' + dateFns.format(tomorrow, 'ddd');
+
+ }
+
+}
+
+
+function buildHourlyTimeTitle(time){
+ var hour=dateFns.getHours(time);
+
+ if (hour===0) {
+ return 'midnight';
+ } else if (hour===12){
+ return 'noon';
+ }
+
+ return dateFns.format(time,'h a');
+}
+
+
+// finds the intervals to report on the hourly forecast
+function calcHourlyReport(data) {
+ var ret = [],
+ targets = [0, 6, 12, 15, 17, 20], // hours that we report
+ current = dateFns.getHours(new Date()),
+ now = new Date(),
+ //firsthour = targets[ getNextHighestIndex(targets, current) ],
+ start,
+ hour, i=0;
+
+ switch (true) {
+ case (current < 3):
+ start = 6;
+ case (current < 9):
+ start = 12; break;
+ case (current < 12):
+ start = 15; break;
+ case (current < 15):
+ start = 17; break;
+ case (current < 17):
+ start = 20; break;
+ case (current < 20):
+ start = 0; break;
+ default:
+ start = 6;
+ }
+
+ while(ret.length<4){
+
+ // hour must be equal or greater than current
+ hour = dateFns.getHours( data[i].startTime );
+ if ( dateFns.isAfter(data[i].startTime, now) && (hour==start || ret.length>0) ) {
+
+ if ( targets.indexOf(hour)>=0 ) { // it is in our target list so record its index
+ ret.push(i);
+ }
+
+ }
+ i++;
+ }
+ return ret;
+}
+
+function mapNWSicons(url){
+ var map = {
+ skc:[26,25],
+ few:[28,27],
+ sct:[24,23],
+ bkn:[22,21],
+ ovc:[20,20],
+ wind_skc:[26,25,47],
+ wind_few:[28,27,47],
+ wind_sct:[24,23,47],
+ wind_bkn:[22,21,47],
+ wind_ovc:[20,20,47],
+ snow:[10,10],
+ rain_snow:[2,2],
+ rain_sleet:[38,38],
+ snow_sleet:[3,3],
+ fzra:[6,6],
+ rain_fzra:[6,6],
+ snow_fzra:[44,44],
+ sleet:[13,13],
+ rain:[8,8],
+ rain_showers:[7,7],
+ rain_showers_hi:[5,5],
+ tsra:[1,1],
+ tsra_sct:[29,37],
+ tsra_hi:[29,37],
+ tornado:[46,46],
+ hurr_warn:[45,45],
+ hurr_watch:[45,45],
+ ts_warn:[45,45],
+ ts_watch:[45,45],
+ ts_hurr_warn:[45,45],
+ dust:[14,14],
+ smoke:[16,16],
+ haze:[16,16],
+ hot:[16,16],
+ cold:[42,42],
+ blizzard:[11,11],
+ fog:[15,15]
+ },
+ matches = url.match(/icons\/land\/(day|night)\/([a-z_]*)\/?([a-z_]*)/), // day or night followed by one or more condition codes
+ idx = {day:0, night:1}[matches[1]],
+ ret=[], match;
+
+ for (i=2; i2) {
+ ret.push( match[2] );
+ }
+ }
+ }
+
+ // place word icons last so they render on top
+ if (ret.length>1 && [15,47,41,42, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 20, 31, 33, 34, 38, 39, 40, 44].indexOf( ret[1] )>-1) {
+ ret.swap(0,1);
+ }
+
+ return ret.map(function(num){
+ return 'images/icons/' + ('0'+num).slice(-2) + '.png';
+ });
+
+}
+
+/*
+
+wind E 14
+gusts 17 mph
+humidity 58%
+dew point 72(degree symbol)
+heat index 95(degree symbol) / wind chill
+pressure 30.02 S
+visibility 10 miles
+uv index High
+partly cloudy
+
+*/
+
+// sample data
+/*
+
+https://query.yahooapis.com/v1/public/yql?format=json&q=select * from weather.forecast where woeid=2402292
+
+"units":{
+ "distance":"mi",
+ "pressure":"in",
+ "speed":"mph",
+ "temperature":"F"
+},
+"title":"Yahoo! Weather - Fargo, ND, US",
+"link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2402292/",
+"description":"Yahoo! Weather for Fargo, ND, US",
+"language":"en-us",
+"lastBuildDate":"Thu, 12 Oct 2017 10:10 PM CDT",
+"ttl":"60",
+"location":{
+ "city":"Fargo",
+ "country":"United States",
+ "region":" ND"
+},
+"wind":{
+ "chill":"52",
+ "direction":"295",
+ "speed":"18"
+},
+"atmosphere":{
+ "humidity":"54",
+ "pressure":"978.0",
+ "rising":"0",
+ "visibility":"16.1"
+},
+"astronomy":{
+ "sunrise":"7:41 am",
+ "sunset":"6:46 pm"
+},
+"image":{
+ "title":"Yahoo! Weather",
+ "width":"142",
+ "height":"18",
+ "link":"http://weather.yahoo.com",
+ "url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"
+},
+"item":{
+ "title":"Conditions for Fargo, ND, US at 09:00 PM CDT",
+ "lat":"46.865089",
+ "long":"-96.829224",
+ "link":"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2402292/",
+ "pubDate":"Thu, 12 Oct 2017 09:00 PM CDT",
+ "condition":{
+ "code":"27",
+ "date":"Thu, 12 Oct 2017 09:00 PM CDT",
+ "temp":"55",
+ "text":"Mostly Cloudy"
+ },
+ "forecast":[
+ {
+ "code":"30",
+ "date":"12 Oct 2017",
+ "day":"Thu",
+ "high":"70",
+ "low":"48",
+ "text":"Partly Cloudy"
+ },
+ {
+ "code":"32",
+ "date":"13 Oct 2017",
+ "day":"Fri",
+ "high":"58",
+ "low":"37",
+ "text":"Sunny"
+ },
+ {
+ "code":"39",
+ "date":"14 Oct 2017",
+ "day":"Sat",
+ "high":"49",
+ "low":"38",
+ "text":"Scattered Showers"
+ },
+ {
+ "code":"34",
+ "date":"15 Oct 2017",
+ "day":"Sun",
+ "high":"56",
+ "low":"31",
+ "text":"Mostly Sunny"
+ },
+ {
+ "code":"34",
+ "date":"16 Oct 2017",
+ "day":"Mon",
+ "high":"65",
+ "low":"35",
+ "text":"Mostly Sunny"
+ },
+ {
+ "code":"34",
+ "date":"17 Oct 2017",
+ "day":"Tue",
+ "high":"65",
+ "low":"39",
+ "text":"Mostly Sunny"
+ },
+ {
+ "code":"30",
+ "date":"18 Oct 2017",
+ "day":"Wed",
+ "high":"64",
+ "low":"48",
+ "text":"Partly Cloudy"
+ },
+ {
+ "code":"30",
+ "date":"19 Oct 2017",
+ "day":"Thu",
+ "high":"65",
+ "low":"44",
+ "text":"Partly Cloudy"
+ },
+ {
+ "code":"30",
+ "date":"20 Oct 2017",
+ "day":"Fri",
+ "high":"66",
+ "low":"49",
+ "text":"Partly Cloudy"
+ },
+ {
+ "code":"28",
+ "date":"21 Oct 2017",
+ "day":"Sat",
+ "high":"61",
+ "low":"49",
+ "text":"Mostly Cloudy"
+ }
+ ],
+ "description":"\n
\nCurrent Conditions:\n
Mostly Cloudy\n
\n
\nForecast:\n
Thu - Partly Cloudy. High: 70Low: 48\n
Fri - Sunny. High: 58Low: 37\n
Sat - Scattered Showers. High: 49Low: 38\n
Sun - Mostly Sunny. High: 56Low: 31\n
Mon - Mostly Sunny. High: 65Low: 35\n
\n
\nFull Forecast at Yahoo! Weather\n
\n
\n
\n]]>",
+ "guid":{
+ "isPermaLink":"false"
+ }
+
+}
+
+
+Current Conditions:\n
Mostly Cloudy\n
\n
\n
+Forecast:\n
Thu - Partly Cloudy. High: 70Low: 48\n
Fri - Sunny. High: 58Low: 37\n
Sat - Scattered Showers. High: 49Low: 38\n
Sun - Mostly Sunny. High: 56Low: 31\n
+Mon - Mostly Sunny. High: 65Low: 35\n
\n
\nFull Forecast at Yahoo! Weather\n
\n
\n
\n]]>",
+ "guid":{
+
+*/
diff --git a/webroot/main.js b/webroot/main.js
new file mode 100644
index 0000000..ec749c4
--- /dev/null
+++ b/webroot/main.js
@@ -0,0 +1,34 @@
+
+$(function(){
+
+ var $main = $("#main"),
+ $window = $( window ),
+ mainHeight = $main.outerHeight(),
+ mainWidth = $main.outerWidth(),
+ mainAspect = 4/3,
+ resizeTimer;
+
+
+ $(window).resize( function(e) {
+ clearTimeout(resizeTimer);
+ resizeTimer = setTimeout(scaleWindow, 100);
+ });
+
+ function scaleWindow() {
+ var scale, windowAspect;
+
+ windowAspect = $window.width() / $window.height();
+
+ if (windowAspect>=mainAspect) {
+ scale = $window.height() / mainHeight;
+ } else {
+ scale = $window.width() / mainWidth;
+ }
+
+ $main.css({
+ transform: "translate(-50%, -50%) " + "scale(" + scale + ")"
+ });
+ }
+ scaleWindow(); // init
+
+});
\ No newline at end of file
diff --git a/webroot/radar.js b/webroot/radar.js
new file mode 100644
index 0000000..236f46a
--- /dev/null
+++ b/webroot/radar.js
@@ -0,0 +1,188 @@
+
+function Radar(divIDin, intervalHoursIn, zoomIn, latitudeIn, longitudeIn, withSat) {
+
+ var map,
+ divID = divIDin,
+ intervalHours = intervalHoursIn,
+ zoom = zoomIn,
+ latitude = latitudeIn,
+ longitude = longitudeIn;
+
+ this.setView = function(lat, long, zoomLevel){
+ map.setView(L.latLng(lat, long), zoomLevel)
+ };
+
+
+ startAnimation();
+ setInterval(updatePeriod, 300000);
+
+ function updatePeriod() {
+ var endDate = roundDate(new Date()),
+ startDate = dateFns.subHours(endDate, 3),
+ newAvailableTimes = L.TimeDimension.Util.explodeTimeRange(startDate, endDate, 'PT5M');
+
+ map.timeDimension.setAvailableTimes(newAvailableTimes, 'replace');
+ map.timeDimension.setCurrentTime(startDate);
+ }
+
+ // snap date to 5 minute intervals
+ function roundDate(date) {
+ date.setUTCMinutes( Math.round(date.getUTCMinutes() / 5) * 5);
+ date.setUTCSeconds(0);
+ return date;
+ }
+
+ function startAnimation () {
+
+ var endDate = roundDate(new Date()),
+ player;
+
+ map = L.map(divID, {
+ zoom: zoom,
+ fullscreenControl: false,
+ timeDimension: true,
+ timeDimensionControl: true,
+ timeDimensionOptions:{
+ timeInterval: "PT" + intervalHours + "H/" + endDate.toISOString(),
+ period: "PT5M",
+ currentTime: endDate
+ },
+
+ timeDimensionControlOptions: {
+ autoPlay: true,
+ playerOptions: {
+ buffer: 36,
+ transitionTime: 100,
+ loop: false,
+ startOver:true
+ }
+ },
+ center: [latitude, longitude] // 31.205482,-82.4331197 test coordinates
+ });
+ map.timeDimensionControl._player.on('stop', function(){
+ setTimeout( function() {
+ map.timeDimensionControl._player.setLooped(true);
+ map.timeDimensionControl._player.start();
+ setTimeout(function(){map.timeDimensionControl._player.setLooped(false)}, 1000);
+ }, 1000)
+ });
+
+
+ // basemap
+ // streets cj9fqw1e88aag2rs2al6m3ko2
+ // satellite streets cj8p1qym6976p2rqut8oo6vxr
+ // weatherscan green cj8owq50n926g2smvagdxg9t8
+ // mapbox://styles/goldbblazez/ckgc7fwvr4qmn19pevtvhyabl
+ // https://api.mapbox.com/styles/v1/goldbblazez/ckgc8lzdz4lzh19qt7q9wbbr9.html?fresh=true&title=copy&access_token=
+ L.tileLayer('https://api.mapbox.com/styles/v1/goldbblazez/ckgc8lzdz4lzh19qt7q9wbbr9/tiles/{z}/{x}/{y}?access_token=putkeyhere', {
+ tileSize: 512,
+ zoomOffset: -1
+ }).addTo(map);
+
+
+ var radarWMS = L.nonTiledLayer.wms("https://nowcoast.noaa.gov/arcgis/services/nowcoast/radar_meteo_imagery_nexrad_time/MapServer/WMSServer", {
+ layers: '1',
+ format: 'image/png',
+ transparent: true,
+ opacity: 0.8
+ });
+
+ if (withSat) {
+
+ var goes_visible_sat = L.nonTiledLayer.wms('https://nowcoast.noaa.gov/arcgis/services/nowcoast/sat_meteo_imagery_time/MapServer/WMSServer', {
+ layers: '9', // 9 for visible sat
+ format: 'image/png',
+ transparent: true,
+ opacity:0.7,
+ useCanvas:true
+ }),
+ satellitetimeLayer = L.timeDimension.layer.wms(goes_visible_sat, {
+ proxy: proxy,
+ updateTimeDimension: false,
+ cache:1
+ });
+
+ satellitetimeLayer.addTo(map).on('timeload',function(t) {
+ var canvas, ctx,
+ imageData, data,
+ i,
+ layers = t.target._layers,
+ keys = Object.keys(layers);
+
+ for (var key of keys) {
+ canvas = layers[key]._bufferCanvas;
+
+ if (canvas.dataset.isAlpha){continue}
+
+ ctx = canvas.getContext('2d');
+
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+
+ var pixels = imageData.data,
+ brighten = 0,
+ contrast = 10;
+ for(var i = 0; i < pixels.length; i+=4){//loop through all data
+
+ pixels[i] += brighten;
+ pixels[i+1] += brighten;
+ pixels[i+2] += brighten;
+
+ var brightness = (pixels[i]+pixels[i+1]+pixels[i+2])/3; //get the brightness
+
+ pixels[i] += brightness > 127 ? contrast : -contrast;
+ pixels[i+1] += brightness > 127 ? contrast : -contrast;
+ pixels[i+2] += brightness > 127 ? contrast : -contrast;
+
+ var rgb = pixels[i] + pixels[i+1] + pixels[i+2];
+ pixels[i] = pixels[i+1] = pixels[i+2] = 255;
+ pixels[i+3] = rgb / 3;
+ }
+ imageData.data = pixels;
+
+ // overwrite original image
+ ctx.putImageData(imageData, 0, 0);
+
+ canvas.dataset.isAlpha = true;
+
+ }
+
+ });
+ }
+
+
+ var proxy = 'js/leaflet/proxy.php';
+ var radarTimeLayer = L.timeDimension.layer.wms(radarWMS, {
+ proxy: proxy,
+ updateTimeDimension: false
+ });
+
+ radarTimeLayer.addTo(map);
+
+
+ }
+}
+
+
+
+
+
+/*
+ * Workaround for 1px lines appearing in some browsers due to fractional transforms
+ * and resulting anti-aliasing.
+ * https://github.com/Leaflet/Leaflet/issues/3575
+ */
+
+(function(){
+ //return;
+ var originalInitTile = L.GridLayer.prototype._initTile
+ L.GridLayer.include({
+ _initTile: function (tile) {
+ originalInitTile.call(this, tile);
+
+ var tileSize = this.getTileSize();
+
+ tile.style.width = tileSize.x + 1 + 'px';
+ tile.style.height = tileSize.y + 1 + 'px';
+ }
+ });
+})()
diff --git a/webroot/slides-loop.js b/webroot/slides-loop.js
new file mode 100644
index 0000000..a2a6bf0
--- /dev/null
+++ b/webroot/slides-loop.js
@@ -0,0 +1,599 @@
+/*
+
+headings:
+RADAR < MAIN CITY < CITY 1 < CITY 2
+*/
+
+ // load slide data
+ function Slides(dataMan) {
+ var radarSlideDuration = 60000,
+ slideDelay = 10000;
+ // for later
+ var selectval = 0;
+ buildHeader();
+
+ setTimeout(nextCity, 5000);
+
+
+ // loop cities
+ function nextCity(){
+
+ advanceHeader();
+
+ var city = $('#info-slides-header .city.current');
+
+ // is radar or city?
+ if (city[0].dataset.woeid) {
+ // show slide deck for the current city
+ showCitySlides( dataMan.location(city[0].dataset.woeid), 0 );
+
+ } else {
+
+
+ // radar
+ showRadar(dataMan.locations[0].lat, dataMan.locations[0].long, 8);
+
+ //setTimeout(function() { weatherAudio.playLocalRadar() }, 2000 );
+
+ // show for how long?
+ setTimeout(nextCity, 60000);
+
+ }
+
+ }
+
+
+
+ function showRadar(lat, long, zoom) {
+
+ weatherMan.mainMap.setView(lat, long, zoom);
+
+ setTimeout(function() {
+
+ // fade out info, fade in radar
+ $('.info-slide-content:visible').fadeOut(250, function(){
+ $('.info-slide').fadeOut(250, function() {
+ $('.radar-slide').fadeIn(500);
+ });
+ });
+
+ }, 1500); // give it time to load
+
+ }
+
+
+ // show the set of slides for one city
+ function showCitySlides(location, idx) {
+
+ var currentDisplay,
+ displays = {
+
+ // Currently (10 sec)
+ currentConditions() {
+ $('.city-info-slide #subhead-title').text('Currently');
+ $('.city-info-slide #subhead-city').text(location.city);
+ var obsData = location.observations,
+ strLabels = 'Humidity
Dew Point
Pressure
Wind
',
+ strData =
+ obsData(0).current.humidity + '%
' + dewPoint(parseInt(obsData(0).current.temp), parseInt(obsData(0).current.humidity)) + '
' + (obsData(0).current.pressure*0.0295301).toFixed(2) + '
' + degToCompass(obsData(0).current.wind_deg) + ' ' + Math.round(parseInt(obsData(0).current.wind_speed)) + '
';
+ if (obsData(0).current.wind_gust!=undefined) {
+ strLabels+='Gusts
';
+ strData+=obsData(0).current.wind_gust + '
';
+ } else {
+ strLabels+='Gusts
';
+ strData+='none
';
+ }
+ var windchill = 35.74 + (0.6215 * parseInt(obsData(0).current.temp)) + (0.4275 * parseInt(obsData(0).current.temp) - 35.75) * parseInt(obsData(0).current.wind_speed) ^ 0.16;
+ if (windchill < parseInt(obsData(0).current.temp)) {
+ strLabels+='Wind Chill';
+ strData+= windchill;
+ } else if (parseInt(obsData(0).current.temp)>=80 && parseInt(obsData(0).current.humidity)>=40 ){
+ strLabels+='Heat Index';
+ return 'heat index ' + heatIndex(obsData(0).current.temp, obsData(0).current.humidity) + '°';
+ };
+
+ $('.city-info .frost-pane .labels').html(strLabels);
+ $('.city-info .frost-pane .data').html(strData);
+
+ // right pane
+ $('.city-info .icon').css('background-image', 'url("' + getCCicon(+obsData(0).current.weather[0].id + obsData(0).current.weather[0].icon, obsData(0).current.wind_speed) + '")');
+ $('.city-info .conditions').text(getCC(obsData(0).current.weather[0].id + obsData(0).current.weather[0].icon, obsData(0).current.wind_speed));
+ $('.city-info .temp').text( Math.round(parseInt(obsData(0).current.temp)) );
+
+ fadeToContent('.city-info');
+ wait(slideDelay);
+
+ }
+
+ // Local Doppler Radar or Radar/Satellite (15 sec, zoomed out with cloud cover)
+ ,localDoppler(){
+ showRadar(location.lat, location.long, 8);
+ wait(slideDelay + 1500);
+ }
+
+ // daypart / hourly
+ ,forecast(fidx) {
+ //pick between day part or local forecast
+ if (selectval === 0 || selectval === 1) {
+ var foreDataHourly = dataMan.locations[0].forecasts('hourly');
+ var indexes = calcHourlyReport(foreDataHourly);
+ var i;
+ var temps=[];
+ // reset tempbar animation
+ $('.info-slide-content.daypart .hour').each(function(){
+ $('.info-slide-content.daypart .hour .tempbar').css("height", "0px")
+ $('.info-slide-content.daypart .hour .tempbar .temp').css("opacity", "0%");
+ $('.info-slide-content.daypart .hour .tempbar .wind').css("opacity", "0%");
+ i = i + 1
+ });
+ //hour title
+ $('.info-slide-content.daypart .hour.i .thing .thingtext').text(buildHourlyTimeTitle(foreDataHourly[indexes[0]].startTime));
+ $('.info-slide-content.daypart .hour.ii .thing .thingtext').text(buildHourlyTimeTitle(foreDataHourly[indexes[1]].startTime));
+ $('.info-slide-content.daypart .hour.iii .thing .thingtext').text(buildHourlyTimeTitle(foreDataHourly[indexes[2]].startTime));
+ $('.info-slide-content.daypart .hour.iv .thing .thingtext').text(buildHourlyTimeTitle(foreDataHourly[indexes[3]].startTime));
+ for (var i of indexes) {
+ var data = foreDataHourly[i];
+ temps.push(data.temperature);
+ }
+
+ $('.info-slide-content.daypart .hour.i .tempbar .temp').text(foreDataHourly[indexes[0]].temperature);
+ $('.info-slide-content.daypart .hour.ii .tempbar .temp').text(foreDataHourly[indexes[1]].temperature);
+ $('.info-slide-content.daypart .hour.iii .tempbar .temp').text(foreDataHourly[indexes[2]].temperature);
+ $('.info-slide-content.daypart .hour.iv .tempbar .temp').text(foreDataHourly[indexes[3]].temperature);
+
+ $('.info-slide-content.daypart .hour.i .tempbar .wind').text(foreDataHourly[indexes[0]].windDirection + ' ' + (foreDataHourly[indexes[0]].windSpeed).replace(" mph", ""));
+ $('.info-slide-content.daypart .hour.ii .tempbar .wind').text(foreDataHourly[indexes[1]].windDirection + ' ' + (foreDataHourly[indexes[1]].windSpeed).replace(" mph", ""));
+ $('.info-slide-content.daypart .hour.iii .tempbar .wind').text(foreDataHourly[indexes[2]].windDirection + ' ' + (foreDataHourly[indexes[2]].windSpeed).replace(" mph", ""));
+ $('.info-slide-content.daypart .hour.iv .tempbar .wind').text(foreDataHourly[indexes[3]].windDirection + ' ' + (foreDataHourly[indexes[3]].windSpeed).replace(" mph", ""));
+
+ $('.info-slide-content.daypart .hour.i .condition').text(buildConditions(foreDataHourly[indexes[0]].shortForecast));
+ $('.info-slide-content.daypart .hour.ii .condition').text(buildConditions(foreDataHourly[indexes[1]].shortForecast));
+ $('.info-slide-content.daypart .hour.iii .condition').text(buildConditions(foreDataHourly[indexes[2]].shortForecast));
+ $('.info-slide-content.daypart .hour.iv .condition').text(buildConditions(foreDataHourly[indexes[3]].shortForecast));
+
+ $('.info-slide-content.daypart .hour.i .icon').css('background-image', 'url("' + mapNWSicons(foreDataHourly[indexes[0]].icon) + '")');
+ $('.info-slide-content.daypart .hour.ii .icon').css('background-image', 'url("' + mapNWSicons(foreDataHourly[indexes[1]].icon) + '")');
+ $('.info-slide-content.daypart .hour.iii .icon').css('background-image', 'url("' + mapNWSicons(foreDataHourly[indexes[2]].icon) + '")');
+ $('.info-slide-content.daypart .hour.iv .icon').css('background-image', 'url("' + mapNWSicons(foreDataHourly[indexes[3]].icon) + '")');
+
+ function buildConditions(forecasttext) {
+ if (forecasttext.includes("Thunderstorms") === true) {
+ return forecasttext.replace(/Slight Chance/g,"Isolated").replace(/Chance/g,"Sct'd").replace(/Thunderstorms/g,"T'storms").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Showers and Thunderstorms/g,"T'Storms")
+ } else {
+ return forecasttext.replace(/Slight Chance/g,"Few").replace(/Chance/g,"").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Isolated/g,"Few")
+ }
+ }
+ function buildHourlyTimeTitle(time){
+ var hour=dateFns.getHours(time);
+
+ if (hour===0) {
+ return 'Midnight';
+ } else if (hour===12){
+ return 'Noon';
+ }
+ return (dateFns.format(time,'h a')).replace(" ", "");
+ }
+ //get reporting hours: 12am, 6am, 12pm, 3pm, 5pm, 8pm...
+ function calcHourlyReport(data) {
+ var ret = [],
+ targets = [0, 6, 12, 15, 17, 20], // hours that we report
+ current = dateFns.getHours(new Date()),
+ now = new Date(),
+ //firsthour = targets[ getNextHighestIndex(targets, current) ],
+ start,
+ hour, i=0;
+
+ switch (true) {
+ case (current < 3):
+ start = 6;
+ case (current < 9):
+ start = 12; break;
+ case (current < 12):
+ start = 15; break;
+ case (current < 14):
+ start = 17; break;
+ case (current < 17):
+ start = 6; break;
+ case (current < 20):
+ start = 6; break;
+ default:
+ start = 6;
+ }
+ while(ret.length<4){
+
+ // hour must be equal or greater than current
+ hour = dateFns.getHours( data[i].startTime );
+ if ( dateFns.isAfter(data[i].startTime, now) && (hour==start || ret.length>0) ) {
+
+ if ( targets.indexOf(hour)>=0 ) { // it is in our target list so record its index
+ ret.push(i);
+ }
+
+ }
+ i++;
+ }
+ return ret;
+ }
+ function buildHourlyHeaderTitle(time) {
+ var today = new Date(),
+ tomorrow = dateFns.addDays(today, 1);
+
+ // title based on the first hour reported
+ switch (dateFns.getHours(time)) {
+
+ case 6: // 6 - Nextday's Forecast / Today's Forecast
+ // if 6am today
+ if (dateFns.isToday(time)) {
+ return "Today's Forecast";
+ }
+ case 0: // 0 - Nextday's Forecast
+ return "Tomorrow's Forecast";
+
+ case 12:
+ return "Today's Forecast";
+
+ case 15:
+ return "Today's Forecast";
+
+ case 17:
+ return "Tonight's Forecast";
+
+ case 20:
+ return "Tonight's Forecast"
+
+ }
+
+ }
+ // calculate height of tempbars
+ $('.city-info-slide #subhead-title').text(buildHourlyHeaderTitle(foreDataHourly[indexes[0]].startTime));
+ fadeToContent('.info-slide-content.daypart')
+ var min = Math.min(...temps), // 54
+ max = Math.max(...temps), // 73
+ range = (max-min),
+ prange = (100-78), // percent range for bar height
+ hourlable = ['i', 'ii', 'iii', 'iv'],
+ temp, value, i = 0;
+ $('.info-slide-content.daypart .hour').each(function(){
+ temp = foreDataHourly[indexes[i]].temperature
+ value = ((temp-min)/range) * prange + 78; // find percentage of range and translate to percent and add that to the starting css % height number
+ valueii = (value/100) * 165 // multiply percentage by max height
+ $('.info-slide-content.daypart .hour.' + hourlable[i] + ' .tempbar').animate({height:valueii+"px"}, 1500,function(){
+ $('.info-slide-content.daypart .hour .tempbar .temp').fadeTo('slow', 1);
+ $('.info-slide-content.daypart .hour .tempbar .wind').fadeTo('slow', 1);
+ });
+ i = i + 1
+ })
+ wait(slideDelay)
+ } else {
+ // Local Forecast -Today (10 sec)
+ var div = '.info-slide-content.forecast ',
+ forecasts = location.forecasts('daily');
+
+ function fillinfo() {
+
+ fidx = (fidx===undefined ? 0 : fidx);
+
+ $('.city-info-slide #subhead-title').text('Local Forecast');
+
+ // title
+ $(div + '.title').text( forecasts[fidx].name );
+
+ // content
+ resizeText( forecasts[fidx].detailedForecast );
+ $(div + '.content').text( forecasts[fidx].detailedForecast );
+
+ }
+
+ fadeToContent(div, fillinfo)
+
+ setTimeout( function() {
+
+ if (fidx<3) {
+ currentDisplay(++fidx);
+ } else {
+ wait(0);
+ }
+
+ }, slideDelay)
+ }
+ selectval = selectval + 1
+ if (selectval === 4) {selectval = 1}
+ }
+
+
+
+ // Extended Forecast(5 day columns)
+ ,extendedForecast() {
+ $('.city-info-slide #subhead-title').text('Extended Forecast');
+
+ var foreDataDaily = dataMan.locations[0].forecasts('daily');
+ var icons, weekend
+ startidx = (foreDataDaily[0].name==='Tonight' ? 1 : 2),
+ days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
+ //days
+ $('.info-slide-content.extended-forecast .frost-pane.iw .thing').text(days[ dateFns.getDay(foreDataDaily[startidx].startTime) ])
+ $('.info-slide-content.extended-forecast .frost-pane.iiw .thing').text(days[ dateFns.getDay(foreDataDaily[startidx+2].startTime) ])
+ $('.info-slide-content.extended-forecast .frost-pane.iiiw .thing').text(days[ dateFns.getDay(foreDataDaily[startidx+4].startTime) ])
+ $('.info-slide-content.extended-forecast .frost-pane.ivw .thing').text(days[ dateFns.getDay(foreDataDaily[startidx+6].startTime) ])
+ $('.info-slide-content.extended-forecast .lfrost-pane.vw .thing .thingtext').text(days[ dateFns.getDay(foreDataDaily[startidx+8].startTime) ])
+
+ //icons
+ $('.info-slide-content.extended-forecast .frost-pane.iw .icon').css('background-image', 'url("' + mapNWSicons(foreDataDaily[startidx].icon)[0] + '")');
+ $('.info-slide-content.extended-forecast .frost-pane.iiw .icon').css('background-image', 'url("' + mapNWSicons(foreDataDaily[startidx+2].icon) + '")');
+ $('.info-slide-content.extended-forecast .frost-pane.iiiw .icon').css('background-image', 'url("' + mapNWSicons(foreDataDaily[startidx+4].icon) + '")');
+ $('.info-slide-content.extended-forecast .frost-pane.ivw .icon').css('background-image', 'url("' + mapNWSicons(foreDataDaily[startidx+6].icon) + '")');
+ $('.info-slide-content.extended-forecast .lfrost-pane.vw .icon').css('background-image', 'url("' + mapNWSicons(foreDataDaily[startidx+8].icon) + '")');
+
+ //conditions
+ $('.info-slide-content.extended-forecast .frost-pane.iw .conditions').text(builddailyconditions(foreDataDaily[startidx].shortForecast));
+ $('.info-slide-content.extended-forecast .frost-pane.iiw .conditions').text(builddailyconditions(foreDataDaily[startidx+2].shortForecast));
+ $('.info-slide-content.extended-forecast .frost-pane.iiiw .conditions').text(builddailyconditions(foreDataDaily[startidx+4].shortForecast));
+ $('.info-slide-content.extended-forecast .frost-pane.ivw .conditions').text(builddailyconditions(foreDataDaily[startidx+6].shortForecast));
+ $('.info-slide-content.extended-forecast .lfrost-pane.vw .conditions').text(builddailyconditions(foreDataDaily[startidx+8].shortForecast));
+
+ //high
+ $('.info-slide-content.extended-forecast .frost-pane.iw .temphigh').text(foreDataDaily[startidx].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.iiw .temphigh').text(foreDataDaily[startidx+2].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.iiiw .temphigh').text(foreDataDaily[startidx+4].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.ivw .temphigh').text(foreDataDaily[startidx+6].temperature)
+ $('.info-slide-content.extended-forecast .lfrost-pane.vw .temphigh .temphightext').text(foreDataDaily[startidx+8].temperature)
+
+ //low
+ $('.info-slide-content.extended-forecast .frost-pane.iw .templow').text(foreDataDaily[startidx+1].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.iiw .templow').text(foreDataDaily[startidx+3].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.iiiw .templow').text(foreDataDaily[startidx+5].temperature)
+ $('.info-slide-content.extended-forecast .frost-pane.ivw .templow').text(foreDataDaily[startidx+7].temperature)
+ $('.info-slide-content.extended-forecast .lfrost-pane.vw .templow').text(foreDataDaily[startidx+9].temperature)
+
+ function builddailyconditions(dailyconditiontext) {
+ if (dailyconditiontext.includes("then") === true){
+ var splitdc = dailyconditiontext.split("then")
+ if (dailyconditiontext.includes("Thunderstorms") === true) {
+ splitdc[0] = splitdc[0].replace(/Slight Chance/g,"Isolated").replace(/Chance/g,"Sct'd").replace(/Thunderstorms/g,"T'storms").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Showers and Thunderstorms/g,"T'Storms").replace(/Patchy/g,"").replace(/Cloudy/g,"Clouds").replace(/Sunny/g,"Sun")
+ splitdc[1] = splitdc[1].replace(/Slight Chance/g,"Isolated").replace(/Chance/g,"Sct'd").replace(/Thunderstorms/g,"T'storms").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Showers and Thunderstorms/g,"T'Storms").replace(/Patchy/g,"").replace(/Cloudy/g,"Clouds").replace(/Sunny/g,"Sun")
+ return "AM" + splitdc[0] + ", PM" + splitdc[1]
+ } else {
+ splitdc[0] = splitdc[0].replace(/Slight Chance/g,"Few").replace(/Chance/g,"").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Isolated/g,"Few").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Showers and Thunderstorms/g,"T'Storms").replace(/Patchy/g,"").replace(/Cloudy/g,"Clouds").replace(/Sunny/g,"Sun")
+ splitdc[1] = splitdc[1].replace(/Slight Chance/g,"Few").replace(/Chance/g,"").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Isolated/g,"Few").replace(/Patchy/g,"").replace(/Cloudy/g,"Clouds").replace(/Sunny/g,"Sun")
+ return "AM" + splitdc[0] + ", PM" + splitdc[1]
+ }
+ } else {
+ if (dailyconditiontext.includes("Thunderstorms") === true) {return dailyconditiontext.replace(/Slight Chance/g,"Isolated").replace(/Chance/g,"Sct'd").replace(/Thunderstorms/g,"T'storms").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Showers and Thunderstorms/g,"T'Storms").replace(/Patchy/g,"")} else {return dailyconditiontext.replace(/Slight Chance/g,"Few").replace(/Chance/g,"").replace(/Partly Sunny/g,"Partly Cloudy").replace(/Isolated/g,"Few").replace(/Patchy/g,"")}
+ }
+
+ }
+ fadeToContent('.info-slide-content.extended-forecast')
+ wait(slideDelay)
+ }
+
+ },
+ keys = Object.keys(displays);
+
+ var daypart;
+ if (idx ') .appendTo($container) .css('font-size', s + 'px') .html(text);
+
+ // have to display parent so we can get measurements
+ $container.closest('.info-slide-content').show();
+
+ $test.width($container.width() );
+ while ($test.outerHeight(true) >= ($container.height()) ) {
+ s -= 1;
+ $test.css('font-size', s + 'px');
+ }
+ $container.closest('.info-slide-content').hide();
+ $container .text(text) .css('font-size', s + 'px');
+ $test.remove();
+
+ }
+
+
+ function fadeToContent(to, callfirst) {
+ var $to = $(to),
+ $parent = $to.closest('.info-slide');
+
+ if ( $parent.is(":hidden") ) {
+ // hide other visible slide then show the parent
+ $to.hide();
+ $('.info-slide:visible').fadeOut(250, function() {
+ //$to.hide();
+ $parent.fadeIn(250, showMe);
+ });
+ } else {
+ hideOldShowMe();
+ }
+
+ function hideOldShowMe() {
+ if ($('.info-slide-content:visible')) {
+ $('.info-slide-content:visible').fadeOut(500, showMe);
+ } else {
+ showMe();
+ }
+ }
+
+ function showMe() {
+ if (callfirst) { callfirst() };
+ $to.fadeIn(500);
+ }
+
+ }
+
+ //doDisplay = displays[ keys[idx] ]();
+
+ // increment the pointer
+ //idx = (++idx===keys.length ? 0 : idx);
+
+ //if (text) {
+ // $('#current-info').html(text);
+ // setTimeout(function(){ displayAtmospheric(idx) }, 6000); // 6 second increment loop
+ //} else {
+ // nothing to display - skip to the next one
+ // setTimeout(function(){ displayAtmospheric(idx) }, 0);
+ //}
+
+ /*
+ (Main City)
+ Currently (10 sec)
+ Local Doppler Radar or Radar/Satellite (15 sec, zoomed out with cloud cover)
+
+ Local Forecast
+ -Today (10 sec)
+ -Tonight (10 sec)
+ -Tomorrow (name day) (10 sec)
+ -Tomorrow (name day) Night (10 sec)
+
+ or
+
+ Daypart
+
+ Extended Forecast(5 day columns)
+ Almanac (to be made)
+
+ (Per City)
+ Local Doppler Radar (center on city)
+ Currently
+ Local Doppler Radar
+ Today's Forecast
+ Extended Forecast(5 day columns)
+ */
+
+
+
+ //idx++;
+ //if (idx<=0){
+ setTimeout(cityLoop, 3000); // change to 60000 for production
+ //} else {
+
+ //}
+
+ }
+
+
+
+ function advanceHeader() {
+
+ // swap current
+ var $cities = $('#info-slides-header .city'),
+ $scroller = $('#info-slides-header .hscroller'),
+ left;
+
+ $($cities[0]).removeClass('current');
+ $($cities[1]).addClass('current');
+
+ // animate move left
+ left = $scroller.position().left - $($cities[1]).position().left;
+ $scroller.animate({ 'left': left+'px' }, 700,
+ function(){
+ // on completion, move the old one to the end
+ $scroller.css('left','');
+ $($cities[0]).appendTo($scroller);
+ $('#info-slides-header span').first().appendTo($scroller);
+ })
+
+
+ }
+
+ function buildHeader(){
+ var city, first, woeid,
+ cities='',
+ arrow='<',
+ radar='LOCAL RADAR';
+
+ for (var location of dataMan.locations) {
+ city = location.city;
+ cities += arrow+'' + city + '';
+ }
+
+ $('#info-slides-header .hscroller').append(cities + arrow + (radar + cities + arrow).repeat(4));
+ }
+
+
+
+
+} // end function
+function mapNWSicons(url){
+ var map = {
+ skc:[26,25],
+ few:[28,27],
+ sct:[24,23],
+ bkn:[22,21],
+ ovc:[20,20],
+ wind_skc:[26,25,47],
+ wind_few:[28,27,47],
+ wind_sct:[24,23,47],
+ wind_bkn:[22,21,47],
+ wind_ovc:[20,20,47],
+ snow:[10,10],
+ rain_snow:[2,2],
+ rain_sleet:[38,38],
+ snow_sleet:[3,3],
+ fzra:[6,6],
+ rain_fzra:[6,6],
+ snow_fzra:[44,44],
+ sleet:[13,13],
+ rain:[8,8],
+ rain_showers:[7,7],
+ rain_showers_hi:[5,5],
+ tsra:[1,1],
+ tsra_sct:[29,37],
+ tsra_hi:[29,37],
+ tornado:[46,46],
+ hurr_warn:[45,45],
+ hurr_watch:[45,45],
+ ts_warn:[45,45],
+ ts_watch:[45,45],
+ ts_hurr_warn:[45,45],
+ dust:[14,14],
+ smoke:[16,16],
+ haze:[16,16],
+ hot:[16,16],
+ cold:[42,42],
+ blizzard:[11,11],
+ fog:[15,15]
+ },
+ matches = url.match(/icons\/land\/(day|night)\/([a-z_]*)\/?([a-z_]*)/), // day or night followed by one or more condition codes
+ idx = {day:0, night:1}[matches[1]],
+ ret=[], match;
+
+ for (i=2; i2) {
+ ret.push( match[2] );
+ }
+ }
+ }
+
+ // place word icons last so they render on top
+ if (ret.length>1 && [15,47,41,42, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 20, 31, 33, 34, 38, 39, 40, 44].indexOf( ret[1] )>-1) {
+ ret.swap(0,1);
+ }
+
+ return ret.map(function(num){
+ return 'images/icons/' + ('0'+num).slice(-2) + '.png';
+ });
+
+}
diff --git a/webroot/utils.js b/webroot/utils.js
new file mode 100644
index 0000000..6dad8a0
--- /dev/null
+++ b/webroot/utils.js
@@ -0,0 +1,113 @@
+// Fisher-Yates shuffle
+function shuffle (array) {
+ var i = 0,
+ j = 0,
+ temp = null;
+
+ for (i = array.length - 1; i > 0; i -= 1) {
+ j = Math.floor(Math.random() * (i + 1));
+ temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+
+ return array;
+}
+
+function getRandom(min, max) {
+ return Math.random() * (max - min) + min;
+}
+
+
+function getNextHighestIndex(arr, value) {
+ var i = arr.length;
+ while (arr[--i] > value);
+ return ++i;
+}
+
+
+function getUrlParameter(e) {
+ return decodeURI((new RegExp(e + "=(.+?)(&|$)").exec(location.search) || [, null])[1])
+}
+
+
+
+// convert celsius to farenheight
+function C2F(c){
+ return Math.round( c * 9 / 5 + 32 );
+}
+
+
+// meters per second to mph
+function mps2mph(meters) {
+ return Math.round( parseFloat(meters) * 2.23694 );
+}
+
+
+// array swap
+Array.prototype.swap = function(a,b){ var tmp=this[a];this[a]=this[b];this[b]=tmp;};
+
+
+function degToCompass(deg){
+ val = Math.round((deg/22.5)+.5);
+ arr=["N","NE","E","SE","S","SW","W","NW"];
+ return arr[(val % 8)];
+}
+
+
+
+function distance(lat1, lon1, lat2, lon2) {
+ var radlat1 = Math.PI * lat1/180,
+ radlat2 = Math.PI * lat2/180,
+ theta = lon1-lon2,
+ radtheta = Math.PI * theta/180,
+ dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
+ dist = Math.acos(dist);
+ dist = dist * 180/Math.PI;
+ dist = dist * 60 * 1.1515;
+ return dist;
+}
+
+
+function dewPoint(tem, r){
+ tem = -1.0*(tem-32)*5/9 ;
+ es = 6.112*Math.exp(-1.0*17.67*tem/(243.5 - tem));
+ ed = r/100.0*es;
+ eln = Math.log(ed/6.112);
+ td = -243.5*eln/(eln - 17.67 );
+ return Math.round( (td*9/5)+32 );
+}
+
+function heatIndex(T, R) { // T = temp, R = relative humidity
+ var T2 = T*T, R2= R*R,
+ c1 = -42.379, c2 = 2.04901523, c3 = 10.14333127,
+ c4 = -0.22475541, c5 = -6.83783*Math.pow(10,-3), c6 = -5.481717*Math.pow(10,-2),
+ c7 = 1.22874*Math.pow(10,-3), c8 = 8.5282*Math.pow(10,-4), c9 = -1.99*Math.pow(10,-6);
+
+ return Math.round(c1 + c2*T + c3 *R + c4*T*R + c5*T2 + c6*R2 + c7*T2*R + c8*T*R2 + c9*T2*R2);
+}
+
+
+// maps current condition code to icon
+function getCCicon(ccCode, windData){
+ var icon = "images/icons/" + ( "0" + {"20011d":1,"20011n":1,"20111d":1,"20111n":1,"20211d":1,"20211n":1,"21011d":46,"21011n":46,"21111d":46,"21111n":46,"21211d":46,"21211n":46,"22111d":1,"22111n":1,"23011d":1,"23011n":1,"23111d":1,"23111n":1,"23211d":1,"23211n":1,"30009d":5,"30009n":5,"30109d":5,"30109n":5,"30209d":5,"30209n":5,"31009d":5,"31009n":5,"31109d":5,"31109n":5,"31209d":5,"31209n":5,"31309d":5,"31309n":5,"31409d":5,"31409n":5,"32109d":5,"32109n":5,"50010d":7,"50010n":7,"50110d":8,"50110n":8,"50210d":31,"50210n":31,"50310d":31,"50310n":31,"50410d":31,"50410d":31,"51110d":6,"51110n":6,"52010d":7,"52010n":7,"52110d":8,"52110n":8,"52210d":31,"52210n":31,"53110d":29,"53110n":37,"60013d":10,"60013n":10,"60113d":12,"60113n":12,"60213d":33,"60213n":33,"61113d":13,"61113n":13,"61213d":13,"61213n":13,"61313d":13,"61313n":13,"61513d":2,"61513n":2,"61613d":2,"61613n":2,"62013d":10,"62013n":10,"62113d":12,"62113n":12,"62213d":33,"62213n":33,"70150d":15,"70150n":15,"71150d":14,"71150n":14,"72150d":16,"72150n":16,"73150d":16,"73150n":16,"74150d":15,"74150n":15,"75150d":16,"75150n":16,"76150d":14,"76150n":14,"76250d":14,"76250n":14,"77150d":18,"77150n":18,"78150d":1,"78150n":1,"80001d":26,"80001n":25,"80102d":28,"80102n":27,"80203d":22,"80203n":21,"80304d":24,"80304n":23,"80404d":20,"80404n":20}[ccCode]).slice(-2) + ".png";
+ if (parseInt(windData) >= 20) {
+ if (ccCode === "50110d" || "50110n" || "52110d" || "52110d") {
+ icon = "images/icons/45.png"
+ }
+ if (ccCode === "60113d" || "60113n" || "62113d" || "62113n") {
+ icon = "images/icons/34.png"
+ }
+ }
+ return icon
+}
+function getCC(ccCode, windData){
+ var condition = {"20011d":"Light Thunderstorm","20011n":"Light Thunderstorm","20111d":"Thunderstorm","20111n":"Thunderstorm","20211d":"Heavy Thunderstorm","20211n":"Heavy Thunderstorm","21011d":"Thunder","21011n":"Thunder","21111d":"Thunder","21111n":"Thunder","21211d":"Thunder","21211n":"Thunder","22111d":"Scattered Thunderstorm","22111n":"Scattered Thunderstorm","23011d":"Light Thunderstorm","23011n":"Light Thunderstorm","23111d":"Light Thunderstorm","23111n":"Light Thunderstorm","23211d":"Light Thunderstorm","23211n":"Light Thunderstorm","30009d":"Drizzle","30009n":"Drizzle","30109d":"Drizzle","30109n":"Drizzle","30209d":"Drizzle","30209n":"Drizzle","31009d":"Drizzle","31009n":"Drizzle","31109d":"Drizzle","31109n":"Drizzle","31209d":"Drizzle","31209n":"Drizzle","31309d":"Drizzle","31309n":"Drizzle","31409d":"Drizzle","31409n":"Drizzle","32109d":"Drizzle","32109n":"Drizzle","50010d":"Light Rain","50010n":"Light Rain","50110d":"Rain","50110n":"Rain","50210d":"Heavy Rain","50210n":"Heavy Rain","50310d":"Heavy Rain","50310n":"Heavy Rain","50410d":"Heavy Rain","50410d":"Heavy Rain","51110d":"Freezing Rain","51110n":"Freezing Rain","52010d":"Light Rain Shower","52010n":"Light Rain Shower","52110d":"Rain Shower","52110n":"Rain Shower","52210d":"Heavy Rain Shower","52210n":"Heavy Rain Shower","53110d":"Scattered Rain Showers","53110n":"Scattered Rain Showers","60013d":"Light Snow","60013n":"Light snow","60113d":"Snow","60113n":"Snow","60213d":"Heavy Snow","60213n":"Heavy Snow","61113d":"Sleet","61113n":"Sleet","61213d":"Sleet","61213n":"Sleet","61313d":"Sleet","61313n":"Sleet","61513d":"Light Rain and Snow","61513n":"Light Rain and Snow","61613d":"Rain and Snow","61613n":"Rain and Snow","62013d":"Light Snow Shower","62013n":"Light Snow Shower","62113d":"Snow Shower","62113n":"Snow Shower","62213d":"Heavy Snow Shower","62213n":"Heavy Snow Shower","70150d":"Mist","70150n":"Mist","71150d":"Smoke","71150n":"Smoke","72150d":"Haze","72150n":"Haze","73150d":"Blowing Dust","73150n":"Blowing Dust","74150d":"Fog","74150n":"Fog","75150d":"Blowing Dust","75150n":"Blowing Dust","76150d":"Blowing Dust","76150n":"Blowing Dust","76250d":"Ash","76250n":"Ash","77150d":"Squalls","77150n":"Squalls","78150d":"Tornado","78150n":"Tornado","80001d":"Sunny","80001n":"Clear","80102d":"Fair","80102n":"Fair","80203d":"Partly Cloudy","80203n":"Partly Cloudy","80304d":"Mostly Cloudy","80304n":"Mostly Cloudy","80404d":"Cloudy","80404n":"Cloudy"}[ccCode]
+ if (parseInt(windData) >= 20) {
+ condition = condition + ", windy"
+ }
+ return condition
+
+}
+
+// https://date-fns.org/docs/Getting-Started
diff --git a/webroot/weather.js b/webroot/weather.js
new file mode 100644
index 0000000..2770937
--- /dev/null
+++ b/webroot/weather.js
@@ -0,0 +1,109 @@
+
+function WeatherManager() {
+
+ var mainloc
+ var mainMap, miniMap, slides,
+ dataMan, loops, // weatherAudio,
+ that = this;
+
+ $(function(){
+
+ // init marquees
+ function refreshMarquee () {
+
+ $('#marquee-container')
+ .marquee('destroy')
+ .marquee({speed: 200, pauseOnHover:true, delayBeforeStart:3000})
+ .on('finished', refreshMarquee);
+ }
+ refreshMarquee();
+
+
+ $('#marquee2').marquee({
+ speed: 170, pauseOnHover: true
+ });
+
+
+ weatherAudio.playCallback = function(tags) {
+ $('.track-info').text('playing "' + tags.title + '" by ' + tags.artist);
+ }
+
+ // this little guy runs the date and time
+ setInterval(
+ function () {
+ var today = new Date();
+
+ $('#date').text( today.toString().slice(4,10).trimRight() );
+ $('#time').text( today.toLocaleTimeString('en-US', { hour: 'numeric', hour12: true, minute: 'numeric', second: 'numeric' }).replace(/ /g,'') );
+ }
+ , 1000);
+
+ initDataPull();
+
+ });
+
+
+
+ function initDataPull() {
+ // get the main location data
+ // on initialization ready, init local forecast loop
+ // on return of fully ready, begin display loops
+
+ // check the url for a specific location
+ var queryString = window.location.search;
+
+ if (queryString) {
+ dataMan = createDataManager( queryString.split("?")[1] );
+ } else {
+
+ // get lat lon from user's ip
+ $.getJSON("http://ip-api.com/json/?callback=?", function(data) {
+ dataMan = createDataManager( data.lat+','+data.lon );
+ mainloc = data.city
+ });
+
+ }
+
+ function initDisplayLoops(){
+ loops = new Loops(dataMan.locations[0]);
+ }
+ function initSlidesLoop() {
+ slides = new Slides(dataMan);
+ }
+ function createDataManager(searchString) {
+ var dataManager = new DataManager();
+
+ $(dataManager)
+ .on('refresh', refreshObservationDisplay)
+ .on('ready:main', initDisplayLoops)
+ .on('allinit', initSlidesLoop);
+
+ dataManager.init(searchString);
+
+ return dataManager;
+
+ }
+
+ }
+
+
+ function refreshObservationDisplay() {
+ var data = dataMan.locations[0].observations(0),
+ cond = data.current.weather[0];
+
+ if (mainMap===undefined) {
+ mainMap = that.mainMap = new Radar("radar-1", 3, 8, data.lat, data.lon, false);
+ miniMap = new Radar("minimap", 3, 5, data.lat, data.lon);
+ }
+
+ $('#city').text(mainloc);
+ $('#forecast-city').text(mainloc + ':');
+ $('#current-temp').text( dataMan.locations[0].temperature() ) ;
+ $('#conditions-icon').css('background-image', 'url("' + getCCicon(cond.id + cond.icon) + '")');
+
+ //weatherAudio.playCurrentConditions(cond);
+
+ }
+
+}
+var weatherMan = new WeatherManager();