Added Extended Forecast, Daypart + general tweaks

This commit is contained in:
buffbears 2021-04-01 21:40:58 -04:00 committed by GitHub
parent 99b802cc2a
commit 960ff44b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 2168 additions and 0 deletions

231
webroot/audio.js Normal file
View File

@ -0,0 +1,231 @@
function WeatherAudio() {
var musicarr = [],
$players = $('<div id="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<len) {
return nextI;
} else {
return (bLoop ? 0 : null);
}
}
}
function initPlayer(id){
var $div;
$div = $('<div id=' + id + ' class="jplayer ' + myclass + '">');
$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;b<f.length;b++)c(f[b]);return c}({1:[function(g,l){var h=g("./stringutils");if("undefined"!==typeof document){var f=document.createElement("script");f.type="text/vbscript";f.textContent="Function IEBinary_getByteAt(strBinary, iOffset)\r\n\tIEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\nEnd Function\r\nFunction IEBinary_getLength(strBinary)\r\n\tIEBinary_getLength = LenB(strBinary)\r\nEnd Function\r\n";
document.getElementsByTagName("head")[0].appendChild(f)}else g("btoa"),g("atob");l.f=function(c,a,b){var m=a||0,d=0;"string"==typeof c?(d=b||c.length,this.a=function(a){return c.charCodeAt(a+m)&255}):"unknown"==typeof c&&(d=b||IEBinary_getLength(c),this.a=function(a){return IEBinary_getByteAt(c,a+m)});this.s=function(a,b){for(var d=Array(b),m=0;m<b;m++)d[m]=this.a(a+m);return d};this.l=function(){return d};this.g=function(a,b){return 0!=(this.a(a)&1<<b)};this.F=function(a){a=(this.a(a+1)<<8)+this.a(a);
0>a&&(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;m<a+b;m++,c++)d[c]=String.fromCharCode(this.a(m));return d.join("")};this.h=function(a,b,d){a=this.s(a,b);switch(d.toLowerCase()){case "utf-16":case "utf-16le":case "utf-16be":d=h.J(a,d);break;
case "utf-8":d=h.K(a);break;default:d=h.I(a)}return d};this.i=function(a,b){b()}}},{"./stringutils":9,atob:void 0,btoa:void 0}],2:[function(g,l){var h=g("./binaryfile");l.f=function(f,c,a){function b(a,b,d,e,c,f){var k=m();k?("undefined"===typeof f&&(f=!0),b&&("undefined"!=typeof k.onload?(k.onload=function(){"200"==k.status||"206"==k.status?(k.fileSize=c||k.getResponseHeader("Content-Length"),b(k)):d&&d({error:"xhr",xhr:k});k=null},d&&(k.onerror=function(){d({error:"xhr",xhr:k});k=null})):k.onreadystatechange=
function(){4==k.readyState&&("200"==k.status||"206"==k.status?(k.fileSize=c||k.getResponseHeader("Content-Length"),b(k)):d&&d({error:"xhr",xhr:k}),k=null)}),k.open("GET",a,f),k.overrideMimeType&&k.overrideMimeType("text/plain; charset=x-user-defined"),e&&k.setRequestHeader("Range","bytes="+e[0]+"-"+e[1]),k.setRequestHeader("If-Modified-Since","Sat, 01 Jan 1970 00:00:00 GMT"),k.send(null)):d&&d({error:"Unable to create XHR object"})}function m(){var a=null;"undefined"===typeof window?a=new (g("xmlhttprequest").XMLHttpRequest):
window.XMLHttpRequest?a=new window.XMLHttpRequest:window.ActiveXObject&&(a=new window.ActiveXObject("Microsoft.XMLHTTP"));return a}function d(a,b,d){var e=m();e?(b&&("undefined"!=typeof e.onload?(e.onload=function(){"200"==e.status||"206"==e.status?b(this):d&&d({error:"xhr",xhr:e});e=null},d&&(e.onerror=function(){d({error:"xhr",xhr:e});e=null})):e.onreadystatechange=function(){4==e.readyState&&("200"==e.status||"206"==e.status?b(this):d&&d({error:"xhr",xhr:e}),e=null)}),e.open("HEAD",a,!0),e.send(null)):
d&&d({error:"Unable to create XHR object"})}function e(d,e){var m,c;function f(a){var b=~~(a[0]/m)-c;a=~~(a[1]/m)+1+c;0>b&&(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(4<e)return{version:">2.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;d<x;){q=null;u=g;n=d;var p=null;switch(w){case 2:k=u.c(n,3);var t=u.w(n+3),z=6;break;case 3:k=u.c(n,4);t=u.m(n+4);z=10;break;case 4:k=u.c(n,4),t=h(n+4,u),z=10}if(""==k)break;d+=z+t;if(!(0>r.indexOf(k))){if(2<w)var p=u,y=n+8,p={message:{Y:p.g(y,6),R:p.g(y,5),V:p.g(y,4)},format:{T:p.g(y+1,7),N:p.g(y+1,3),P:p.g(y+1,2),L:p.g(y+1,1),C:p.g(y+1,0)}};n+=z;p&&p.format.C&&(h(n,u),n+=4,t-=4);p&&p.format.L||(k in f.b?q=f.b[k]:"T"==k[0]&&
(q=f.b["T*"]),q=q?q(n,t,u,p):void 0,q={id:k,size:t,description:k in f.frames?f.frames[k]:"Unknown",data:q},k in l?(l[k].id&&(l[k]=[l[k]]),l[k].push(q)):l[k]=q)}}k=l}for(var B in c)if(c.hasOwnProperty(B)){a:{t=c[B];"string"==typeof t&&(t=[t]);z=0;for(d=void 0;d=t[z];z++)if(d in k){b=k[d].data;break a}b=void 0}b&&(e[B]=b)}for(var C in k)k.hasOwnProperty(C)&&(e[C]=k[C]);return e};l.f=f},{"./id3v2frames":7}],7:[function(g,l){function h(a){var b;switch(a){case 0:b="iso-8859-1";break;case 1:b="utf-16";
break;case 2:b="utf-16be";break;case 3:b="utf-8"}return b}var f={b:{}},c="32x32 pixels 'file icon' (PNG only);Other file icon;Cover (front);Cover (back);Leaflet page;Media (e.g. lable side of CD);Lead artist/lead performer/soloist;Artist/performer;Conductor;Band/Orchestra;Composer;Lyricist/text writer;Recording Location;During recording;During performance;Movie/video screen capture;A bright coloured fish;Illustration;Band/artist logotype;Publisher/Studio logotype".split(";");f.b.APIC=function(a,b,
m,d,e){e=e||"3";d=a;var f=h(m.a(a));switch(e){case "2":var g=m.c(a+1,3);a+=4;break;case "3":case "4":g=m.h(a+1,b-(a-d),""),a+=1+g.j}e=m.a(a,1);e=c[e];f=m.h(a+1,b-(a-d),f);a+=1+f.j;return{format:g.toString(),type:e,description:f.toString(),data:m.s(a,d+b-a)}};f.b.COMM=function(a,b,c){var d=a,e=h(c.a(a)),f=c.c(a+1,3),g=c.h(a+4,b-4,e);a+=4+g.j;a=c.h(a,d+b-a,e);return{language:f,X:g.toString(),text:a.toString()}};f.b.COM=f.b.COMM;f.b.PIC=function(a,b,c,d){return f.b.APIC(a,b,c,d,"2")};f.b.PCNT=function(a,
b,c){return c.S(a)};f.b.CNT=f.b.PCNT;f.b["T*"]=function(a,b,c){var d=h(c.a(a));return c.h(a+1,b-1,d).toString()};f.b.TCON=function(a,b,c){return f.b["T*"].apply(this,arguments).replace(/^\(\d+\)/,"")};f.b.TCO=f.b.TCON;f.b.USLT=function(a,b,c){var d=a,e=h(c.a(a)),f=c.c(a+1,3),g=c.h(a+4,b-4,e);a+=4+g.j;a=c.h(a,d+b-a,e);return{language:f,O:g.toString(),U:a.toString()}};f.b.ULT=f.b.USLT;l.f=f},{}],8:[function(g,l){function h(a,b,f,d){var e=a.m(b);if(0==e)d();else{var g=a.c(b+4,4);-1<["moov","udta","meta",
"ilst"].indexOf(g)?("meta"==g&&(b+=4),a.i([b+8,b+8+8],function(){h(a,b+8,e-8,d)})):a.i([b+(g in c.o?0:e),b+e+8],function(){h(a,b+e,f,d)})}}function f(a,b,h,d,e){e=void 0===e?"":e+" ";for(var g=h;g<h+d;){var l=b.m(g);if(0==l)break;var v=b.c(g+4,4);if(-1<["moov","udta","meta","ilst"].indexOf(v)){"meta"==v&&(g+=4);f(a,b,g+8,l-8,e);break}if(c.o[v]){var w=b.w(g+16+1),x=c.o[v],w=c.types[w];if("trkn"==v)a[x[0]]=b.a(g+16+11),a.count=b.a(g+16+13);else{var v=g+16+4+4,q=l-16-4-4,k;switch(w){case "text":k=b.h(v,
q,"UTF-8");break;case "uint8":k=b.F(v);break;case "jpeg":case "png":k={format:"image/"+w,data:b.s(v,q)}}a[x[0]]="comment"===x[0]?{text:k}:k}}g+=l}}var c={types:{0:"uint8",1:"text",13:"jpeg",14:"png",21:"uint8"},o:{"\u00a9alb":["album"],"\u00a9art":["artist"],"\u00a9ART":["artist"],aART:["artist"],"\u00a9day":["year"],"\u00a9nam":["title"],"\u00a9gen":["genre"],trkn:["track"],"\u00a9wrt":["composer"],"\u00a9too":["encoder"],cprt:["copyright"],covr:["picture"],"\u00a9grp":["grouping"],keyw:["keyword"],
"\u00a9lyr":["lyrics"],"\u00a9cmt":["comment"],tmpo:["tempo"],cpil:["compilation"],disk:["disc"]},u:function(a,b){a.i([0,7],function(){h(a,0,a.l(),b)})},v:function(a){var b={};f(b,a,0,a.l());return b}};l.f=c},{}],9:[function(g,l){l.f={J:function(h,f,c){var a=0,b=1,g=0;c=Math.min(c||h.length,h.length);254==h[0]&&255==h[1]?(f=!0,a=2):255==h[0]&&254==h[1]&&(f=!1,a=2);f&&(b=0,g=1);f=[];for(var d=0;a<c;d++){var e=h[a+b],l=(e<<8)+h[a+g],a=a+2;if(0==l)break;else 216>e||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;c<f;b++){var g=h[c++];if(0==g)break;else if(128>g)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<f;){var b=g[a++];if(0==b)break;c[a-1]=String.fromCharCode(b)}c=new String(c.join(""));c.j=a;return c}}},{}]},{},[4])(4)});

114
webroot/data-manager.js Normal file
View File

@ -0,0 +1,114 @@
function DataManager(pointSearch){
var $this = $(this),
that = this,
excludeRadiusMiles=5;
includeRadiusMiles=50;
var _locations = [];
this.locations = _locations;
this.location = function(woeid) {
return _locations.find(x => 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');
}
}
}

4
webroot/date_fns.min.js vendored Normal file

File diff suppressed because one or more lines are too long

75
webroot/groupull.js Normal file
View File

@ -0,0 +1,75 @@
function GroupDataManager() {
var locations =
[
{name:'Chicago', n2:'IL'},
{name:'Minneapolis', n2:'MN'},
{name:'Tempe', n2:'AZ'},
{name:'Fargo', n2:'ND'},
{name:'North Hollywood', n2:'CA'},
{name:'Los Angeles', n2:'CA'},
{name:'Huntington Beach'},
{name:'Las Vegas', n2:'NV'},
{name:'Honolulu', n2:'HI'},
{name:'Orlando', n2:'FL'},
{name:'New York', n2:'NY'},
{name:'Napa', n2:'CA'},
{name:'Montego Bay', n2:''},
{name:'Kona', n2:'HI'},
{name:'Kalipaki Beach', n2:''},
{name:'Ixtapa', n2:'MX'}
]
;
checkRefresh();
setInterval(checkRefresh, 300000);
// check to see if data needs to be refreshed
function checkRefresh() {
var woeid, location;
for (location of locations) {
// check the expiration
if (location.hasOwnProperty('xdate') && dateFns.isFuture(location.xdate)) { continue; }
woeid = location.hasOwnProperty('woeid') ? location.woeid : '';
// woeid is the id for the location to pull data for
var url = 'https://api.openweathermap.org/data/2.5/weather?q='+ location.name + '&appid=putkeyhere&units=imperial'
pullData(url, location);
}
}
function pullData(url, location) {
var $span;
// ajax the latest observation
$.getJSON(url, function(data) {
location.data = data;
if ( !location.hasOwnProperty('woeid') ) {
location.woeid = location.data.id;
$span = $("<span id='" + location.woeid + "'></span>").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;

179
webroot/location.js Normal file
View File

@ -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));
});
}
}
}

522
webroot/loops.js Normal file
View File

@ -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 ) + '&deg;'; },
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 + '&deg;';
} 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) + '&deg;';
}
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 = $("<div class='forecast-tile daily" + weekend + "'></div>");
$("<div class='header'></div>") .appendTo(newtile) .text(days[ dateFns.getDay(foreDataDaily[i].startTime) ]);
icons = mapNWSicons(foreDataDaily[i].icon);
for (x=icons.length-1; x>=0; x--){
$("<img class='icon' src=''/>") .appendTo(newtile) .attr('src', icons[x]);
}
$("<div class='high'></div>") .appendTo(newtile) .text(foreDataDaily[i].temperature);
$("<div class='low'></div>") .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 = $("<div class='forecast-tile hourly'></div>");
sizer = $("<div class='width-sizer'></div>").appendTo(newtile);
icons = mapNWSicons(data.icon);
for (var x=icons.length-1; x>=0; x--){
$("<img class='icon' src=''/>") .appendTo(sizer) .attr('src', icons[x]);
}
$("<div class='footer'></div>") .appendTo(newtile) .text(buildHourlyTimeTitle(data.startTime));
highbar = $("<div class='hourly-high'></div>") .appendTo(sizer);
$("<div class='high'></div>") .appendTo(highbar) .text(data.temperature);
temps.push(data.temperature);
$("<div class='temp-bar'></div>") .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 = $('<div style="position:absolute;top:100%;"></div>') .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; i<matches.length; i++){
if (matches[i]) {
match = map[ matches[i] ];
ret.push( match[idx] );
// some icons are 2 layered
if (match.length>2) {
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":"<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/27.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Mostly Cloudy\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Thu - Partly Cloudy. High: 70Low: 48\n<BR /> Fri - Sunny. High: 58Low: 37\n<BR /> Sat - Scattered Showers. High: 49Low: 38\n<BR /> Sun - Mostly Sunny. High: 56Low: 31\n<BR /> Mon - Mostly Sunny. High: 65Low: 35\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2402292/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n<BR />\n]]>",
"guid":{
"isPermaLink":"false"
}
}
Current Conditions:</b>\n<BR />Mostly Cloudy\n<BR />\n<BR />\n<b>
Forecast:</b>\n<BR /> Thu - Partly Cloudy. High: 70Low: 48\n<BR /> Fri - Sunny. High: 58Low: 37\n<BR /> Sat - Scattered Showers. High: 49Low: 38\n<BR /> Sun - Mostly Sunny. High: 56Low: 31\n<BR />
Mon - Mostly Sunny. High: 65Low: 35\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2402292/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n<BR />\n]]>",
"guid":{
*/

34
webroot/main.js Normal file
View File

@ -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
});

188
webroot/radar.js Normal file
View File

@ -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';
}
});
})()

599
webroot/slides-loop.js Normal file
View File

@ -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<br>Dew Point<br>Pressure<Br>Wind<br>',
strData =
obsData(0).current.humidity + '%<br>' + dewPoint(parseInt(obsData(0).current.temp), parseInt(obsData(0).current.humidity)) + '<br>' + (obsData(0).current.pressure*0.0295301).toFixed(2) + '<br>' + degToCompass(obsData(0).current.wind_deg) + ' ' + Math.round(parseInt(obsData(0).current.wind_speed)) + '<br>';
if (obsData(0).current.wind_gust!=undefined) {
strLabels+='Gusts<Br>';
strData+=obsData(0).current.wind_gust + '<br>';
} else {
strLabels+='Gusts<Br>';
strData+='none<br>';
}
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) + '&deg;';
};
$('.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<keys.length) {
currentDisplay = displays[keys[idx]];
currentDisplay();
} else { // done - exit
nextCity();
}
return;
function wait(duration){
setTimeout(function() {
showCitySlides(location, ++idx);
}, duration);
}
function resizeText(text){
var s = 50,
$container = $('.info-slide-content.forecast .content'),
$test = $('<div style="position:absolute;top:100%;"></div>') .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='<span class="divider-arrow">&lt;</span>',
radar='<span class="city radar">LOCAL RADAR</span>';
for (var location of dataMan.locations) {
city = location.city;
cities += arrow+'<span class="city" data-woeid="' + location.woeid + '">' + city + '</span>';
}
$('#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; i<matches.length; i++){
if (matches[i]) {
match = map[ matches[i] ];
ret.push( match[idx] );
//some icons are 2 layered but don't want it
if (match.length>2) {
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';
});
}

113
webroot/utils.js Normal file
View File

@ -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

109
webroot/weather.js Normal file
View File

@ -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();