diff --git a/.gitignore b/.gitignore index f91ec08..1708808 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,7 @@ *.swp *.pyc *.log -js/mobile.js -js/init_plot.js -js/version.json -css/mobile.css -cache.manifest -tiles/ \ No newline at end of file +/index.html +/service-worker.js +/js/version.json +tiles/ diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 3d763d3..0000000 --- a/.htaccess +++ /dev/null @@ -1,38 +0,0 @@ - - # Compress HTML, CSS, JavaScript, Text, XML and fonts - AddOutputFilterByType DEFLATE application/javascript - AddOutputFilterByType DEFLATE application/rss+xml - AddOutputFilterByType DEFLATE application/vnd.ms-fontobject - AddOutputFilterByType DEFLATE application/x-font - AddOutputFilterByType DEFLATE application/x-font-opentype - AddOutputFilterByType DEFLATE application/x-font-otf - AddOutputFilterByType DEFLATE application/x-font-truetype - AddOutputFilterByType DEFLATE application/x-font-ttf - AddOutputFilterByType DEFLATE application/x-javascript - AddOutputFilterByType DEFLATE application/xhtml+xml - AddOutputFilterByType DEFLATE application/xml - AddOutputFilterByType DEFLATE font/opentype - AddOutputFilterByType DEFLATE font/otf - AddOutputFilterByType DEFLATE font/ttf - AddOutputFilterByType DEFLATE image/svg+xml - AddOutputFilterByType DEFLATE image/x-icon - AddOutputFilterByType DEFLATE text/css - AddOutputFilterByType DEFLATE text/html - AddOutputFilterByType DEFLATE text/javascript - AddOutputFilterByType DEFLATE text/plain - AddOutputFilterByType DEFLATE text/xml - - # Remove browser bugs (only needed for ancient browsers) - BrowserMatch ^Mozilla/4 gzip-only-text/html - BrowserMatch ^Mozilla/4\.0[678] no-gzip - BrowserMatch \bMSIE !no-gzip !gzip-only-text/html - Header append Vary User-Agent - - -AddType text/cache-manifest .manifest -AddType text/cache-manifest .appcache - -AddType application/x-font-woff .woff -AddType application/x-font-ttf .ttf -AddType application/vnd.ms-fontobject .eot -AddType image/svg+xml .svg diff --git a/DEVELOPER_README.md b/DEVELOPER_README.md index 3272308..6f38d04 100644 --- a/DEVELOPER_README.md +++ b/DEVELOPER_README.md @@ -4,6 +4,6 @@ To get a copy of the code and run a test web server: 1. [Fork the repository](https://github.com/projecthorus/sondehub-tracker/fork) by visiting [https://github.com/projecthorus/sondehub-tracker/fork](https://github.com/projecthorus/sondehub-tracker/fork). 2. Clone the repository with your git tool of choice. -3. Run `build.sh` to compile the javascript files. (This requires Java to be installed and in your path.) +3. Run `build.sh` to generate `index.html`, `service-worker.js`, and `js/version.json`. 4. Run `python serve.py` to run a simple web server to (This requires python 3.x) 5. Visit [http://localhost:8000](http://localhost:8000) to view the local version of the server! diff --git a/Dockerfile b/Dockerfile index fbb1bdb..53107d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:latest -RUN apk add --no-cache python3 openjdk11 sed git +RUN apk add --no-cache python3 sed git WORKDIR /app ADD . . diff --git a/README.md b/README.md index 2a812de..42427be 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,6 @@ Pull requests are welcome. ## Installation -Requirements: Java - $ git clone https://github.com/projecthorus/sondehub-tracker.git $ ./build.sh $ python serve.py diff --git a/build.sh b/build.sh index ecabd0e..948cde1 100755 --- a/build.sh +++ b/build.sh @@ -1,50 +1,22 @@ #!/bin/bash -# compile stylesheet -echo -n "Compiling CSS... " -cd css -rm -f mobile.css -cat base.css skeleton.css layout.css habitat-font.css main.css leaflet.css leaflet.fullscreen.css skewt.css > mobile.tmp -java -jar "../tools/yuicompressor-2.4.8.jar" --type=css mobile.tmp > mobile.css -rm -f mobile.tmp -cd .. -echo "Done!" - -#compile javascript -echo -n "Compiling JavaScript... " -cd js -rm -f mobile.js init_plot.js -# precompiled libs -cat jquery* >> mobile.js +set -e VERSION="`git rev-parse --short HEAD`" - BUILD_DATE="`date -u +%Y-%m-%dT%H:%M:%SZ`" -# compile the rest -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge iscroll.js >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge chasecar.lib.js | sed "s/{VER}/$VERSION/" >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge tracker.js >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge app.js | sed "s/{VER}/$VERSION/" | sed "s/{BUILD_DATE}/$BUILD_DATE/" >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge colour-map.js >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge xdata.js >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge station.js >> mobile.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge format.js >> mobile.js - -#compile plot lib and config -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge _jquery.flot.js >> init_plot.js -java -jar "../tools/yuicompressor-2.4.8.jar" --type=js --disable-optimizations --nomunge plot_config.js >> init_plot.js - # create version check file -echo "{\"build_date\": \"$BUILD_DATE\", \"version\": \"$VERSION\", \"refresh\": 86400}" > version.json - -cd .. +echo -n "Generating js/version.json... " +echo "{\"build_date\": \"$BUILD_DATE\", \"version\": \"$VERSION\", \"refresh\": 86400}" > js/version.json echo "Done!" # cache fixes -sed -i'' "s/{VER}/$VERSION/" index.html -sed -i'' "s/{VER}/$VERSION/" service-worker.js +echo -n "Generating index.html... " +sed -e "s/{VER}/$VERSION/" -e "s/{BUILD_DATE}/$BUILD_DATE/" index.template.html > index.html +echo "Done!" +echo -n "Generating service-worker.js... " +sed -e "s/{VER}/$VERSION/" service-worker.template.js > service-worker.js echo "Done!" echo "Build version: $VERSION Build date: $BUILD_DATE" diff --git a/css/base.css b/css/base.css index 8c20088..016fe3e 100644 --- a/css/base.css +++ b/css/base.css @@ -29,257 +29,572 @@ /* #Reset & Basics (Inspired by E. Meyers) ================================================== */ - html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; } - sup { - font-size: smaller; - vertical-align: +0.4em; } - sub { - font-size: smaller; - vertical-align: -0.25em; } - article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { - display: block; } - body { - line-height: 1; } - ol, ul { - list-style: none; } - blockquote, q { - quotes: none; } - blockquote:before, blockquote:after, - q:before, q:after { - content: ''; - content: none; } - table { - border-collapse: collapse; - border-spacing: 0; } +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +sup { + font-size: smaller; + vertical-align: +0.4em; +} + +sub { + font-size: smaller; + vertical-align: -0.25em; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} + +body { + line-height: 1; +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} /* #Basic Styles ================================================== */ - body { - background: #fff; - font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #666; - -webkit-font-smoothing: antialiased; /* Fix for webkit rendering */ - -webkit-text-size-adjust: 100%; - } +body { + background: #fff; + font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #666; + -webkit-font-smoothing: antialiased; + /* Fix for webkit rendering */ + -webkit-text-size-adjust: 100%; +} /* #Typography ================================================== */ - h1, h2, h3, h4, h5, h6 { - font-weight: normal; } - h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } - h1 { font-size: 46px; line-height: 50px; margin-bottom: 14px;} - h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; } - h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; } - h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; } - h5 { font-size: 17px; line-height: 24px; } - h6 { font-size: 14px; line-height: 21px; } - .subheader { color: #777; } - - p { margin: 0 0 20px 0; } - p img { margin: 0; } - p.lead { font-size: 21px; line-height: 27px; color: #777; } - - em { font-style: italic; } - strong { font-weight: bold; color: #333; } - b { font-weight: bold; } - small { font-size: 80%; } +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: normal; +} + +h1 a, +h2 a, +h3 a, +h4 a, +h5 a, +h6 a { + font-weight: inherit; +} + +h1 { + font-size: 46px; + line-height: 50px; + margin-bottom: 14px; +} + +h2 { + font-size: 35px; + line-height: 40px; + margin-bottom: 10px; +} + +h3 { + font-size: 28px; + line-height: 34px; + margin-bottom: 8px; +} + +h4 { + font-size: 21px; + line-height: 30px; + margin-bottom: 4px; +} + +h5 { + font-size: 17px; + line-height: 24px; +} + +h6 { + font-size: 14px; + line-height: 21px; +} + +.subheader { + color: #777; +} + +p { + margin: 0 0 20px 0; +} + +p img { + margin: 0; +} + +p.lead { + font-size: 21px; + line-height: 27px; + color: #777; +} + +em { + font-style: italic; +} + +strong { + font-weight: bold; + color: #333; +} + +b { + font-weight: bold; +} + +small { + font-size: 80%; +} /* Blockquotes */ - blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; } - blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; } - blockquote cite { display: block; font-size: 12px; color: #555; } - blockquote cite:before { content: "\2014 \0020"; } - blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; } - - hr { border: solid #ddd; border-width: 1px 0 0; clear: both; height: 0; } +blockquote, +blockquote p { + font-size: 17px; + line-height: 24px; + color: #777; + font-style: italic; +} + +blockquote { + margin: 0 0 20px; + padding: 9px 20px 0 19px; + border-left: 1px solid #ddd; +} + +blockquote cite { + display: block; + font-size: 12px; + color: #555; +} + +blockquote cite:before { + content: "\2014 \0020"; +} + +blockquote cite a, +blockquote cite a:visited, +blockquote cite a:visited { + color: #555; +} + +hr { + border: solid #ddd; + border-width: 1px 0 0; + clear: both; + height: 0; +} /* #Links ================================================== */ - a, a:visited { color: #333; text-decoration: underline; outline: 0; } - a:hover, a:focus { color: #000; } - p a, p a:visited { line-height: inherit; } +a, +a:visited { + color: #333; + text-decoration: underline; + outline: 0; +} + +a:hover, +a:focus { + color: #000; +} + +p a, +p a:visited { + line-height: inherit; +} /* #Lists ================================================== */ - ul, ol { margin-bottom: 20px; } - ul { list-style: none outside; } - ol { list-style: decimal; } - ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } - ul.square { list-style: square outside; } - ul.circle { list-style: circle outside; } - ul.disc { list-style: disc outside; } - ul ul, ul ol, - ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; } - ul ul li, ul ol li, - ol ol li, ol ul li { margin-bottom: 6px; } - li { line-height: 18px; margin-bottom: 12px; } - ul.large li { line-height: 21px; } - li p { line-height: 21px; } +ul, +ol { + margin-bottom: 20px; +} + +ul { + list-style: none outside; +} + +ol { + list-style: decimal; +} + +ol, +ul.square, +ul.circle, +ul.disc { + margin-left: 30px; +} + +ul.square { + list-style: square outside; +} + +ul.circle { + list-style: circle outside; +} + +ul.disc { + list-style: disc outside; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin: 4px 0 5px 30px; + font-size: 90%; +} + +ul ul li, +ul ol li, +ol ol li, +ol ul li { + margin-bottom: 6px; +} + +li { + line-height: 18px; + margin-bottom: 12px; +} + +ul.large li { + line-height: 21px; +} + +li p { + line-height: 21px; +} /* #Images ================================================== */ - img.scale-with-grid { - max-width: 100%; - height: auto; } +img.scale-with-grid { + max-width: 100%; + height: auto; +} /* #Buttons ================================================== */ - .button, - button, - input[type="submit"], - input[type="reset"], - input[type="button"] { - background: #eee; /* Old browsers */ - background: #eee -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */ - background: #eee -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.2)), color-stop(100%,rgba(0,0,0,.2))); /* Chrome,Safari4+ */ - background: #eee -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */ - background: #eee -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */ - background: #eee -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */ - background: #eee linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */ - border: 1px solid #aaa; - border-top: 1px solid #ccc; - border-left: 1px solid #ccc; - margin-right: 1px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - color: #444; - display: inline-block; - font-size: 11px; - font-weight: bold; - text-decoration: none; - text-shadow: 0 1px rgba(255, 255, 255, .75); - cursor: pointer; - line-height: normal; - padding: 8px 10px; - font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; } - - .button:hover, - button:hover, - input[type="submit"]:hover, - input[type="reset"]:hover, - input[type="button"]:hover { - color: #222; - background: #ddd; /* Old browsers */ - background: #ddd -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */ - background: #ddd -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.3)), color-stop(100%,rgba(0,0,0,.3))); /* Chrome,Safari4+ */ - background: #ddd -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */ - background: #ddd -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */ - background: #ddd -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */ - background: #ddd linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */ - border: 1px solid #888; - border-top: 1px solid #aaa; - border-left: 1px solid #aaa; } - - .button:active, - button:active, - input[type="submit"]:active, - input[type="reset"]:active, - input[type="button"]:active { - border: 1px solid #666; - background: #ccc; /* Old browsers */ - background: #ccc -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */ - background: #ccc -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.35)), color-stop(100%,rgba(10,10,10,.4))); /* Chrome,Safari4+ */ - background: #ccc -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */ - background: #ccc -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */ - background: #ccc -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */ - background: #ccc linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ } - - .button.full-width, - button.full-width, - input[type="submit"].full-width, - input[type="reset"].full-width, - input[type="button"].full-width { - width: 100%; - padding-left: 0 !important; - padding-right: 0 !important; - text-align: center; } - - /* Fix for odd Mozilla border & padding issues */ - button::-moz-focus-inner, - input::-moz-focus-inner { - border: 0; - padding: 0; - } - - .button.disabled, - button.disabled, - input[type="submit"].disabled, - input[type="reset"].disabled, - input[type="button"].disabled { - border: 1px solid #aaa; - color: #aaa; - background: #fff; - } +.button, +button, +input[type="submit"], +input[type="reset"], +input[type="button"] { + background: #eee; + /* Old browsers */ + background: #eee -moz-linear-gradient(top, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .2) 100%); + /* FF3.6+ */ + background: #eee -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255, 255, 255, .2)), color-stop(100%, rgba(0, 0, 0, .2))); + /* Chrome,Safari4+ */ + background: #eee -webkit-linear-gradient(top, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .2) 100%); + /* Chrome10+,Safari5.1+ */ + background: #eee -o-linear-gradient(top, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .2) 100%); + /* Opera11.10+ */ + background: #eee -ms-linear-gradient(top, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .2) 100%); + /* IE10+ */ + background: #eee linear-gradient(top, rgba(255, 255, 255, .2) 0%, rgba(0, 0, 0, .2) 100%); + /* W3C */ + border: 1px solid #aaa; + border-top: 1px solid #ccc; + border-left: 1px solid #ccc; + margin-right: 1px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + color: #444; + display: inline-block; + font-size: 11px; + font-weight: bold; + text-decoration: none; + text-shadow: 0 1px rgba(255, 255, 255, .75); + cursor: pointer; + line-height: normal; + padding: 8px 10px; + font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.button:hover, +button:hover, +input[type="submit"]:hover, +input[type="reset"]:hover, +input[type="button"]:hover { + color: #222; + background: #ddd; + /* Old browsers */ + background: #ddd -moz-linear-gradient(top, rgba(255, 255, 255, .3) 0%, rgba(0, 0, 0, .3) 100%); + /* FF3.6+ */ + background: #ddd -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255, 255, 255, .3)), color-stop(100%, rgba(0, 0, 0, .3))); + /* Chrome,Safari4+ */ + background: #ddd -webkit-linear-gradient(top, rgba(255, 255, 255, .3) 0%, rgba(0, 0, 0, .3) 100%); + /* Chrome10+,Safari5.1+ */ + background: #ddd -o-linear-gradient(top, rgba(255, 255, 255, .3) 0%, rgba(0, 0, 0, .3) 100%); + /* Opera11.10+ */ + background: #ddd -ms-linear-gradient(top, rgba(255, 255, 255, .3) 0%, rgba(0, 0, 0, .3) 100%); + /* IE10+ */ + background: #ddd linear-gradient(top, rgba(255, 255, 255, .3) 0%, rgba(0, 0, 0, .3) 100%); + /* W3C */ + border: 1px solid #888; + border-top: 1px solid #aaa; + border-left: 1px solid #aaa; +} + +.button:active, +button:active, +input[type="submit"]:active, +input[type="reset"]:active, +input[type="button"]:active { + border: 1px solid #666; + background: #ccc; + /* Old browsers */ + background: #ccc -moz-linear-gradient(top, rgba(255, 255, 255, .35) 0%, rgba(10, 10, 10, .4) 100%); + /* FF3.6+ */ + background: #ccc -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255, 255, 255, .35)), color-stop(100%, rgba(10, 10, 10, .4))); + /* Chrome,Safari4+ */ + background: #ccc -webkit-linear-gradient(top, rgba(255, 255, 255, .35) 0%, rgba(10, 10, 10, .4) 100%); + /* Chrome10+,Safari5.1+ */ + background: #ccc -o-linear-gradient(top, rgba(255, 255, 255, .35) 0%, rgba(10, 10, 10, .4) 100%); + /* Opera11.10+ */ + background: #ccc -ms-linear-gradient(top, rgba(255, 255, 255, .35) 0%, rgba(10, 10, 10, .4) 100%); + /* IE10+ */ + background: #ccc linear-gradient(top, rgba(255, 255, 255, .35) 0%, rgba(10, 10, 10, .4) 100%); + /* W3C */ +} + +.button.full-width, +button.full-width, +input[type="submit"].full-width, +input[type="reset"].full-width, +input[type="button"].full-width { + width: 100%; + padding-left: 0 !important; + padding-right: 0 !important; + text-align: center; +} + +/* Fix for odd Mozilla border & padding issues */ +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +.button.disabled, +button.disabled, +input[type="submit"].disabled, +input[type="reset"].disabled, +input[type="button"].disabled { + border: 1px solid #aaa; + color: #aaa; + background: #fff; +} /* #Forms ================================================== */ - fieldset { - margin-bottom: 20px; } - input[type="text"], - input[type="password"], - input[type="email"], - textarea, - select { - border: 1px solid #ccc; - padding: 6px 4px; - outline: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #888; - margin: 0; - max-width: 100%; - background: #fff; } - select { - padding: 0; } - input[type="text"]:focus, - input[type="password"]:focus, - input[type="email"]:focus, - textarea:focus { - border: 1px solid #aaa; - color: #444; - -moz-box-shadow: 0 0 3px rgba(0,0,0,.2); - -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2); - box-shadow: 0 0 3px rgba(0,0,0,.2); } - textarea { - min-height: 60px; } - label, - legend { - display: block; - font-weight: bold; - font-size: 13px; } - select { - width: 220px; } - input[type="checkbox"] { - display: inline; } - label span, - legend span { - font-weight: normal; - font-size: 13px; - color: #444; } +fieldset { + margin-bottom: 20px; +} + +input[type="text"], +input[type="password"], +input[type="email"], +textarea, +select { + border: 1px solid #ccc; + padding: 6px 4px; + outline: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #888; + margin: 0; + max-width: 100%; + background: #fff; +} + +input[type="text"]:focus, +input[type="password"]:focus, +input[type="email"]:focus, +textarea:focus { + border: 1px solid #aaa; + color: #444; + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, .2); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .2); + box-shadow: 0 0 3px rgba(0, 0, 0, .2); +} + +textarea { + min-height: 60px; +} + +label, +legend { + display: block; + font-weight: bold; + font-size: 13px; +} + +select { + width: 220px; +} + +input[type="checkbox"] { + display: inline; +} + +label span, +legend span { + font-weight: normal; + font-size: 13px; + color: #444; +} /* #Misc ================================================== */ - .remove-bottom { margin-bottom: 0 !important; } - .half-bottom { margin-bottom: 10px !important; } - .add-bottom { margin-bottom: 20px !important; } - .no-margin { margin: 0 !important; padding: 0; } - - +.remove-bottom { + margin-bottom: 0 !important; +} + +.half-bottom { + margin-bottom: 10px !important; +} + +.add-bottom { + margin-bottom: 20px !important; +} + +.no-margin { + margin: 0 !important; + padding: 0; +} \ No newline at end of file diff --git a/css/layout.css b/css/layout.css index 2b915f2..8ebfd8f 100644 --- a/css/layout.css +++ b/css/layout.css @@ -179,4 +179,20 @@ header h1{ padding: 10px; border-width: 1px; display: none; +} + +#banner { + display: none +} +@media only screen and (min-width: 768px){ + #banner { + line-height: 1.1; + font-size: small; + display: block; + margin-left: 300px; + text-align: center; + overflow-y: auto; + font-weight: bold; + height: 100%; + } } \ No newline at end of file diff --git a/css/main.css b/css/main.css index e273a77..ee57d7f 100644 --- a/css/main.css +++ b/css/main.css @@ -99,11 +99,13 @@ body { #timebox { top: 7px; right: 5px; + width: 205px; } #lookanglesbox { top: 40px; right: 5px; + width: 205px; } #timebox.past svg path { diff --git a/img/apple-touch-icon.png b/img/apple-touch-icon.png index b16c0d7..380f8a3 100644 Binary files a/img/apple-touch-icon.png and b/img/apple-touch-icon.png differ diff --git a/img/graw_logo_small.png b/img/graw_logo_small.png index 4d1043e..dedb4f3 100644 Binary files a/img/graw_logo_small.png and b/img/graw_logo_small.png differ diff --git a/img/icons/icon_x192.png b/img/icons/icon_x192.png index 618ae3a..bcf4bac 100644 Binary files a/img/icons/icon_x192.png and b/img/icons/icon_x192.png differ diff --git a/img/icons/icon_x512.png b/img/icons/icon_x512.png index 9654aad..5795d0f 100644 Binary files a/img/icons/icon_x512.png and b/img/icons/icon_x512.png differ diff --git a/img/icons/maskable_icon_x128.png b/img/icons/maskable_icon_x128.png index ed395e1..1101d10 100644 Binary files a/img/icons/maskable_icon_x128.png and b/img/icons/maskable_icon_x128.png differ diff --git a/img/icons/maskable_icon_x192.png b/img/icons/maskable_icon_x192.png index 1ec5586..87b550d 100644 Binary files a/img/icons/maskable_icon_x192.png and b/img/icons/maskable_icon_x192.png differ diff --git a/img/icons/maskable_icon_x384.png b/img/icons/maskable_icon_x384.png index ef558c9..31c9d5c 100644 Binary files a/img/icons/maskable_icon_x384.png and b/img/icons/maskable_icon_x384.png differ diff --git a/img/icons/maskable_icon_x48.png b/img/icons/maskable_icon_x48.png index 56ae4a7..dad2250 100644 Binary files a/img/icons/maskable_icon_x48.png and b/img/icons/maskable_icon_x48.png differ diff --git a/img/icons/maskable_icon_x512.png b/img/icons/maskable_icon_x512.png index f00d755..8dff53a 100644 Binary files a/img/icons/maskable_icon_x512.png and b/img/icons/maskable_icon_x512.png differ diff --git a/img/icons/maskable_icon_x72.png b/img/icons/maskable_icon_x72.png index 1388186..34e3a9e 100644 Binary files a/img/icons/maskable_icon_x72.png and b/img/icons/maskable_icon_x72.png differ diff --git a/img/icons/maskable_icon_x96.png b/img/icons/maskable_icon_x96.png index febd7c0..4d7b7a3 100644 Binary files a/img/icons/maskable_icon_x96.png and b/img/icons/maskable_icon_x96.png differ diff --git a/img/logo.png b/img/logo.png deleted file mode 100644 index cc06274..0000000 Binary files a/img/logo.png and /dev/null differ diff --git a/img/markers/balloon-adafruit.png b/img/markers/balloon-adafruit.png deleted file mode 100644 index 4c715f2..0000000 Binary files a/img/markers/balloon-adafruit.png and /dev/null differ diff --git a/img/markers/balloon-blue.png b/img/markers/balloon-blue.png deleted file mode 100644 index 9888487..0000000 Binary files a/img/markers/balloon-blue.png and /dev/null differ diff --git a/img/markers/balloon-buzz.png b/img/markers/balloon-buzz.png deleted file mode 100644 index 17097b5..0000000 Binary files a/img/markers/balloon-buzz.png and /dev/null differ diff --git a/img/markers/balloon-cyan.png b/img/markers/balloon-cyan.png deleted file mode 100644 index 9eae707..0000000 Binary files a/img/markers/balloon-cyan.png and /dev/null differ diff --git a/img/markers/balloon-green.png b/img/markers/balloon-green.png deleted file mode 100644 index 126a0b0..0000000 Binary files a/img/markers/balloon-green.png and /dev/null differ diff --git a/img/markers/balloon-invisible.png b/img/markers/balloon-invisible.png deleted file mode 100644 index 19464e9..0000000 Binary files a/img/markers/balloon-invisible.png and /dev/null differ diff --git a/img/markers/balloon-iss.png b/img/markers/balloon-iss.png deleted file mode 100644 index 23d2a49..0000000 Binary files a/img/markers/balloon-iss.png and /dev/null differ diff --git a/img/markers/balloon-orange.png b/img/markers/balloon-orange.png deleted file mode 100644 index b0c5d09..0000000 Binary files a/img/markers/balloon-orange.png and /dev/null differ diff --git a/img/markers/balloon-purple.png b/img/markers/balloon-purple.png deleted file mode 100644 index e129b1c..0000000 Binary files a/img/markers/balloon-purple.png and /dev/null differ diff --git a/img/markers/balloon-red.png b/img/markers/balloon-red.png deleted file mode 100644 index f079bfe..0000000 Binary files a/img/markers/balloon-red.png and /dev/null differ diff --git a/img/markers/balloon-rob.png b/img/markers/balloon-rob.png deleted file mode 100644 index 819a558..0000000 Binary files a/img/markers/balloon-rob.png and /dev/null differ diff --git a/img/markers/balloon-rpi.png b/img/markers/balloon-rpi.png deleted file mode 100644 index c340631..0000000 Binary files a/img/markers/balloon-rpi.png and /dev/null differ diff --git a/img/markers/balloon-shockpink.png b/img/markers/balloon-shockpink.png deleted file mode 100644 index 49ad112..0000000 Binary files a/img/markers/balloon-shockpink.png and /dev/null differ diff --git a/img/markers/balloon-thereg.png b/img/markers/balloon-thereg.png deleted file mode 100644 index f26068f..0000000 Binary files a/img/markers/balloon-thereg.png and /dev/null differ diff --git a/img/markers/balloon-yellow.png b/img/markers/balloon-yellow.png deleted file mode 100644 index 66fc042..0000000 Binary files a/img/markers/balloon-yellow.png and /dev/null differ diff --git a/img/markers/balloon.svg b/img/markers/balloon.svg new file mode 100755 index 0000000..8966505 --- /dev/null +++ b/img/markers/balloon.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/markers/car-blue.png b/img/markers/car-blue.png deleted file mode 100644 index 09192f0..0000000 Binary files a/img/markers/car-blue.png and /dev/null differ diff --git a/img/markers/car-green.png b/img/markers/car-green.png deleted file mode 100644 index c42da50..0000000 Binary files a/img/markers/car-green.png and /dev/null differ diff --git a/img/markers/car-purple.png b/img/markers/car-purple.png deleted file mode 100644 index a281513..0000000 Binary files a/img/markers/car-purple.png and /dev/null differ diff --git a/img/markers/car-red.png b/img/markers/car-red.png deleted file mode 100644 index ce44c92..0000000 Binary files a/img/markers/car-red.png and /dev/null differ diff --git a/img/markers/car-teal.png b/img/markers/car-teal.png deleted file mode 100644 index f0e6fe8..0000000 Binary files a/img/markers/car-teal.png and /dev/null differ diff --git a/img/markers/car-yellow.png b/img/markers/car-yellow.png deleted file mode 100644 index 5c35476..0000000 Binary files a/img/markers/car-yellow.png and /dev/null differ diff --git a/img/markers/car.svg b/img/markers/car.svg new file mode 100644 index 0000000..c277ce8 --- /dev/null +++ b/img/markers/car.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/markers/parachute-blue.png b/img/markers/parachute-blue.png deleted file mode 100644 index ff4dc5d..0000000 Binary files a/img/markers/parachute-blue.png and /dev/null differ diff --git a/img/markers/parachute-cyan.png b/img/markers/parachute-cyan.png deleted file mode 100644 index bf83a49..0000000 Binary files a/img/markers/parachute-cyan.png and /dev/null differ diff --git a/img/markers/parachute-green.png b/img/markers/parachute-green.png deleted file mode 100644 index f41d7e1..0000000 Binary files a/img/markers/parachute-green.png and /dev/null differ diff --git a/img/markers/parachute-orange.png b/img/markers/parachute-orange.png deleted file mode 100644 index e3f2d16..0000000 Binary files a/img/markers/parachute-orange.png and /dev/null differ diff --git a/img/markers/parachute-purple.png b/img/markers/parachute-purple.png deleted file mode 100644 index adae959..0000000 Binary files a/img/markers/parachute-purple.png and /dev/null differ diff --git a/img/markers/parachute-red.png b/img/markers/parachute-red.png deleted file mode 100644 index 603c60f..0000000 Binary files a/img/markers/parachute-red.png and /dev/null differ diff --git a/img/markers/parachute-rpi.png b/img/markers/parachute-rpi.png deleted file mode 100644 index ad421b6..0000000 Binary files a/img/markers/parachute-rpi.png and /dev/null differ diff --git a/img/markers/parachute-yellow.png b/img/markers/parachute-yellow.png deleted file mode 100644 index 4d85a51..0000000 Binary files a/img/markers/parachute-yellow.png and /dev/null differ diff --git a/img/markers/parachute.svg b/img/markers/parachute.svg new file mode 100644 index 0000000..8b10853 --- /dev/null +++ b/img/markers/parachute.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/markers/payload-blue.png b/img/markers/payload-blue.png deleted file mode 100644 index c03caee..0000000 Binary files a/img/markers/payload-blue.png and /dev/null differ diff --git a/img/markers/payload-cyan.png b/img/markers/payload-cyan.png deleted file mode 100644 index 226bfa8..0000000 Binary files a/img/markers/payload-cyan.png and /dev/null differ diff --git a/img/markers/payload-green.png b/img/markers/payload-green.png deleted file mode 100644 index f524a73..0000000 Binary files a/img/markers/payload-green.png and /dev/null differ diff --git a/img/markers/payload-orange.png b/img/markers/payload-orange.png deleted file mode 100644 index 20e5b05..0000000 Binary files a/img/markers/payload-orange.png and /dev/null differ diff --git a/img/markers/payload-purple.png b/img/markers/payload-purple.png deleted file mode 100644 index 80e1e1a..0000000 Binary files a/img/markers/payload-purple.png and /dev/null differ diff --git a/img/markers/payload-recovery-planned.png b/img/markers/payload-recovery-planned.png new file mode 100644 index 0000000..e314714 Binary files /dev/null and b/img/markers/payload-recovery-planned.png differ diff --git a/img/markers/payload-red.png b/img/markers/payload-red.png deleted file mode 100644 index 7ad8acc..0000000 Binary files a/img/markers/payload-red.png and /dev/null differ diff --git a/img/markers/payload-rpi.png b/img/markers/payload-rpi.png deleted file mode 100644 index 9f533fb..0000000 Binary files a/img/markers/payload-rpi.png and /dev/null differ diff --git a/img/markers/payload-yellow.png b/img/markers/payload-yellow.png deleted file mode 100644 index e9b7bb5..0000000 Binary files a/img/markers/payload-yellow.png and /dev/null differ diff --git a/img/markers/payload.svg b/img/markers/payload.svg new file mode 100644 index 0000000..b89d7d6 --- /dev/null +++ b/img/markers/payload.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/markers/target-blue.png b/img/markers/target-blue.png deleted file mode 100644 index fae5796..0000000 Binary files a/img/markers/target-blue.png and /dev/null differ diff --git a/img/markers/target-cyan.png b/img/markers/target-cyan.png deleted file mode 100644 index ba30743..0000000 Binary files a/img/markers/target-cyan.png and /dev/null differ diff --git a/img/markers/target-green.png b/img/markers/target-green.png deleted file mode 100644 index 56b4682..0000000 Binary files a/img/markers/target-green.png and /dev/null differ diff --git a/img/markers/target-orange.png b/img/markers/target-orange.png deleted file mode 100644 index 4b353b6..0000000 Binary files a/img/markers/target-orange.png and /dev/null differ diff --git a/img/markers/target-purple.png b/img/markers/target-purple.png deleted file mode 100644 index 4510158..0000000 Binary files a/img/markers/target-purple.png and /dev/null differ diff --git a/img/markers/target-red.png b/img/markers/target-red.png deleted file mode 100644 index 5987df9..0000000 Binary files a/img/markers/target-red.png and /dev/null differ diff --git a/img/markers/target-yellow.png b/img/markers/target-yellow.png deleted file mode 100644 index 8dc2d08..0000000 Binary files a/img/markers/target-yellow.png and /dev/null differ diff --git a/img/markers/target.svg b/img/markers/target.svg new file mode 100644 index 0000000..e80875e --- /dev/null +++ b/img/markers/target.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/img/sondehub_au.png b/img/sondehub_au.png new file mode 100644 index 0000000..069c3c0 Binary files /dev/null and b/img/sondehub_au.png differ diff --git a/img/sondehub_supporters.kra b/img/sondehub_supporters.kra index 901db8e..6728b1b 100644 Binary files a/img/sondehub_supporters.kra and b/img/sondehub_supporters.kra differ diff --git a/img/splash/splash-wide.png b/img/splash/splash-wide.png index f5766e9..59b1fcb 100644 Binary files a/img/splash/splash-wide.png and b/img/splash/splash-wide.png differ diff --git a/index.html b/index.template.html similarity index 87% rename from index.html rename to index.template.html index 9ad87fb..964bc91 100644 --- a/index.html +++ b/index.template.html @@ -9,6 +9,7 @@ + @@ -19,15 +20,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +
- - + +
@@ -56,21 +84,70 @@ no
location
+ @@ -292,7 +369,7 @@

Chase Mode

Callsign - +
Notice: If you enable this, your location will be uploaded to SondeHub, making it publicly visible on the map for up to 12 hours. @@ -323,48 +400,7 @@

Chase Mode

none

-

Report Recovery

-
-
- You can report a recovery here. Enter the serial number (e.g. 'S1234567'), tick recovered/not recovered, and add some notes. Click here for information on how to locate a sonde's serial number. If the sonde is not on the map, type its serial into the top-left search box and press enter to retrieve historical data. You can then mark it as recovered. Optionally, you can use your location instead of the sonde's last location. -
-
- Serial - -
-
- Recovery Success -
- - -
-
-
- Use Car Position -
- - -
-
-
- Notes -
-
- - - - -
-
- Report Result - none -
-
-
-
-
- -
+ Recovery reporting is now on the recoveries tab! (Balloon icon) @@ -374,6 +410,9 @@

Report Recovery

Zoom in for realtime data!
+ -
This version of SondeHub is out of date. Click here to reload. Page will reload automatically in -
+
This version of SondeHub is out of date. Click here to reload. Page will reload automatically in
+ - + + + - - - - - - - - - - - - diff --git a/js/app.js b/js/app.js index 351cf18..2731e23 100644 --- a/js/app.js +++ b/js/app.js @@ -43,6 +43,9 @@ function lhash_update(history_step) { if(wvar.nena) { hash += "&nena=1"; } + if(wvar.box) { // We just need something to change so we can trigger the back button correctly + hash += "&box="+wvar.box; + } hash = encodeURI(hash); // set state @@ -70,6 +73,7 @@ var wvar = { nyan: false, nena: false, site: 0, + box: "" }; @@ -93,6 +97,7 @@ function load_hash(no_refresh) { query: "", nyan: false, nena: false, + box: "" }; parms.forEach(function(v) { @@ -102,23 +107,28 @@ function load_hash(no_refresh) { switch(k) { case "mt": - if( baseMaps.hasOwnProperty(v) ) { + if( baseMaps.hasOwnProperty(v) && map) { map.removeLayer(baseMaps[selectedLayer]); selectedLayer = v; map.addLayer(baseMaps[v]); } break; case "mz": - map.setZoom(parseInt(v)); + if (map){ + map.setZoom(parseInt(v)); + } break; case "mc": def.zoom = false; manual_pan = true; v = v.split(','); var latlng = new L.LatLng(v[0], v[1]); - map.panTo(latlng); + if (map){ + map.panTo(latlng); + } break; case "f": + v = v.toUpperCase(); refocus = (follow_vehicle != v); follow_vehicle = v; def.focus = v; @@ -128,6 +138,7 @@ function load_hash(no_refresh) { if(modeList.indexOf(def.mode) == -1) def.mode = (is_mobile) ? modeDefaultMobile : modeDefault; break; case "q": + v = v.toUpperCase(); def.query = v; $("header .search input[type='text']").val(v); break; @@ -141,6 +152,9 @@ function load_hash(no_refresh) { focusID = v; gotoSite(v); break; + case "box": + def[k] = v; + break; } }); @@ -149,22 +163,34 @@ function load_hash(no_refresh) { if(wvar[k] != def[k]) refresh = true; }); + + $.extend(true, wvar, def); + if(map){ + if (!def["box"]){ + $(".flatpage").hide() + } else { + $(".flatpage").hide() + $("#"+def["box"]).show() + } + checkSize(); + wvar["box"]= def["box"]; - // force refresh - if(!no_refresh) { - if(refresh) { - zoomed_in = false; - clean_refresh(wvar.mode, true); - } - else if(refocus) { - $(".row.active").removeClass('active'); - $(".vehicle"+vehicles[wvar.focus].uuid).addClass('active'); - followVehicle(wvar.focus, manual_pan, true); - } - } + // force refresh + if(!no_refresh) { + if(refresh) { + zoomed_in = false; + clean_refresh(wvar.mode, true); + } + else if(refocus) { + $(".row.active").removeClass('active'); + $(".vehicle"+vehicles[wvar.focus].uuid).addClass('active'); + followVehicle(wvar.focus, manual_pan, true); + } + } - lhash_update(); + lhash_update(); + } } window.onhashchange = load_hash; @@ -198,24 +224,38 @@ for(var idx in params) { // loads the tracker interface function trackerInit() { + // update current position if we geolocation is available + if(currentPosition) updateCurrentPosition(currentPosition.lat, currentPosition.lon); + + + + if (currentPosition && manual_pan == false){ + coords = [currentPosition.lat, currentPosition.lon] + zoomLevel = 9; + map.setView(coords, zoomLevel, {animate: false}); + } $('#loading,#settingsbox,#aboutbox,#chasebox').hide(); // welcome screen $('header,#main').show(); // interface elements checkSize(); - if(map) return; + // if we there is enough screen space open aboutbox on startup + if(!is_mobile && !offline.get('opt_nowelcome') && $(window).width() > 900) $('.nav li.about').click(); + if(is_mobile || wvar.enabled) $(".nav .wvar").hide(); if(!is_mobile) { - $.getScript("js/init_plot.js", function() { checkSize(); if(!map) load(); }); + $.getScript("js/_jquery.flot.js", function() { + $.getScript("js/plot_config.js", function() { checkSize(); if(!map) load(); }); + }); if(wvar.graph) $('#telemetry_graph').attr('style',''); return; } - if(!map) load(); + lhash_update(true); } -// if for some reason, applicationCache is not working, load the app after a 3s timeout +// load the app after a 3s timeout var initTimer = setTimeout(trackerInit, 3000); var listScroll; @@ -299,8 +339,7 @@ function positionUpdateError(error) { switch(error.code) { case error.PERMISSION_DENIED: - alert("no permission to use your location"); - $('#sw_chasecar').click(); // turn off chase car + $('#sw_chasecar').removeClass('on').addClass('off'); break; default: break; @@ -315,89 +354,83 @@ var positionUpdateHandle = function(position) { } } - //navigator.geolocation.getCurrentPosition(function(position) { - var lat = position.coords.latitude; - var lon = position.coords.longitude; - var alt = (position.coords.altitude) ? position.coords.altitude : 0; - var accuracy = (position.coords.accuracy) ? position.coords.accuracy : 0; - var speed = (position.coords.speed) ? position.coords.speed : 0; - - // constantly update 'last updated' field, and display friendly time since last update - if(!GPS_ts) { - GPS_ts = parseInt(position.timestamp/1000); - - setInterval(function() { - var delta_ts = parseInt(Date.now()/1000) - GPS_ts; - - // generate friendly timestamp - var hours = Math.floor(delta_ts / 3600); - var minutes = Math.floor(delta_ts / 60) % 60; - var ts_str = (delta_ts >= 60) ? - ((hours)?hours+'h ':'') + - ((minutes)?minutes+'m':'') + - ' ago' - : 'just now'; - $('#cc_timestamp').text(ts_str); - }, 30000); - - $('#cc_timestamp').text('just now'); - } + var lat = position.coords.latitude; + var lon = position.coords.longitude; + var alt = (position.coords.altitude) ? position.coords.altitude : 0; + var accuracy = (position.coords.accuracy) ? position.coords.accuracy : 0; + var speed = (position.coords.speed) ? position.coords.speed : 0; + + // constantly update 'last updated' field, and display friendly time since last update + if(!GPS_ts) { + GPS_ts = parseInt(position.timestamp/1000); + + setInterval(function() { + var delta_ts = parseInt(Date.now()/1000) - GPS_ts; + + // generate friendly timestamp + var hours = Math.floor(delta_ts / 3600); + var minutes = Math.floor(delta_ts / 60) % 60; + var ts_str = (delta_ts >= 60) ? + ((hours)?hours+'h ':'') + + ((minutes)?minutes+'m':'') + + ' ago' + : 'just now'; + $('#cc_timestamp').text(ts_str); + }, 30000); + + $('#cc_timestamp').text('just now'); + } - // save position and update only if different is available - if(CHASE_timer < (new Date()).getTime() && - ( - GPS_lat != lat || - GPS_lon != lon || - GPS_alt != alt || - GPS_speed != speed - ) + // save position and update only if different is available + if(CHASE_timer < (new Date()).getTime() && + ( + GPS_lat != lat || + GPS_lon != lon || + GPS_alt != alt || + GPS_speed != speed ) - { - GPS_lat = lat; - GPS_lon = lon; - GPS_alt = alt; - GPS_speed = speed; - GPS_ts = parseInt(position.timestamp/1000); - $('#cc_timestamp').text('just now'); - - // update look angles once we get position - if(follow_vehicle !== null && vehicles[follow_vehicle] !== undefined) { - update_lookangles(follow_vehicle); - } + ) + { + GPS_lat = lat; + GPS_lon = lon; + GPS_alt = alt; + GPS_speed = speed; + GPS_ts = parseInt(position.timestamp/1000); + $('#cc_timestamp').text('just now'); + + // update look angles once we get position + if(follow_vehicle !== null && vehicles[follow_vehicle] !== undefined) { + update_lookangles(follow_vehicle); + } - if(CHASE_enabled) { - ChaseCar.updatePosition(callsign, position); - CHASE_timer = (new Date()).getTime() + 15000; - } + if(CHASE_enabled) { + ChaseCar.updatePosition(callsign, position); + CHASE_timer = (new Date()).getTime() + 15000; } - else { return; } - - // add/update marker on the map (tracker.js) - updateCurrentPosition(lat, lon); - - // round the coordinates - lat = parseInt(lat * 10000)/10000; // 4 decimal places (11m accuracy at equator) - lon = parseInt(lon * 10000)/10000; // 4 decimal places - speed = parseInt(speed * 10)/10; // 1 decimal place - accuracy = parseInt(accuracy); - alt = parseInt(alt); - - // dispaly them in the top right corner - $('#app_name b').html(lat + '
' + lon); - - // update chase car interface - $('#cc_lat').text(lat); - $('#cc_lon').text(lon); - $('#cc_alt').text(alt + " m"); - $('#cc_accuracy').text(accuracy + " m"); - $('#cc_speed').text(speed + " m/s"); - /* - }, - function() { - // when there is no location - $('#app_name b').html('mobile
tracker'); - }); - */ + } + + + // add/update marker on the map (sondehub.js) + updateCurrentPosition(lat, lon); + + // round the coordinates + lat = parseInt(lat * 10000)/10000; // 4 decimal places (11m accuracy at equator) + lon = parseInt(lon * 10000)/10000; // 4 decimal places + speed = parseInt(speed * 10)/10; // 1 decimal place + accuracy = parseInt(accuracy); + alt = parseInt(alt); + + // dispaly them in the top right corner + $('#app_name b').html(lat + '
' + lon); + + // update chase car interface + $('#cc_lat').text(lat); + $('#cc_lon').text(lon); + $('#cc_alt').text(alt + " m"); + $('#cc_accuracy').text(accuracy + " m"); + $('#cc_speed').text(speed + " m/s"); + + }; var twoZeroPad = function(n) { @@ -445,6 +478,27 @@ var format_time_friendly = function(start, end) { } }; +var format_coordinates = function(lat, lon, name) { + var coords_text; + var ua = navigator.userAgent.toLowerCase(); + var span = document.createElement('span') + var a = document.createElement("a") + span.appendChild(a) + // determine how to link the coordinates to a native app, if on a mobile device + if(ua.indexOf('iphone') > -1) { + a.href = 'maps://?q='+lat+','+lon + } else if(ua.indexOf('android') > -1) { + a.href = 'geo:'+lat+','+lon+'?q='+lat+','+lon+'('+name+')' + } else { + a.href = 'https://www.google.com/maps/search/?api=1&query='+lat+','+lon + a.target="_blank" + a.rel="noopener noreferrer" + } + a.innerText = roundNumber(lat, 5) + ', ' + roundNumber(lon, 5) + + return span.innerHTML; +}; + // runs every second var updateTime = function(date) { // update timebox @@ -466,18 +520,12 @@ var updateTime = function(date) { } }; -const version = "{VER}"; - $(window).ready(function() { // refresh timebox setInterval(function() { updateTime(new Date()); }, 1000); - // Update Tracker version info - $('#build_version').text(version); - $('#build_date').text("{BUILD_DATE}"); - // resize elements if needed checkSize(); @@ -553,12 +601,6 @@ $(window).ready(function() { $("#main").removeClass("drag"); }); - // confirm dialog when launchnig a native map app with coordinates - //$('#main').on('click', '#launch_mapapp', function() { - // var answer = confirm("Launch your maps app?"); - // return answer; - //}); - // follow vehicle by clicking on data $('#main').on('click', '.row .data', function() { var e = $(this).parent(); @@ -633,6 +675,14 @@ $(window).ready(function() { default: pretty_name = name[0].toUpperCase() + name.slice(1); } } + var visible_box = $(".flatpage:visible"); + if (visible_box.length > 0){ + wvar.box = visible_box[0].id + } else { + wvar.box = null + } + + lhash_update(true); checkSize(); }); @@ -646,7 +696,6 @@ $(window).ready(function() { field.removeAttr('disabled'); e.removeClass('on').addClass('off'); - if(navigator.geolocation) navigator.geolocation.clearWatch(CHASE_enabled); CHASE_enabled = null; //CHASE_enabled = false; @@ -654,7 +703,7 @@ $(window).ready(function() { if(currentPosition && currentPosition.marker) map.addLayer(currentPosition.marker); // turning the switch on } else { - if(callsign.length == null || callsign.length < 3) { alert('Please enter a valid callsign, at least 3 characters'); return; } + if(callsign == null || callsign.length < 3) { alert('Please enter a valid callsign, at least 3 characters'); return; } if(!callsign.match(/^[a-zA-Z0-9\_\-]+$/)) { alert('Invalid characters in callsign (use only a-z,0-9,-,_)'); return; } field.attr('disabled','disabled'); @@ -674,8 +723,7 @@ $(window).ready(function() { ChaseCar.updatePosition(callsign, { coords: { latitude: GPS_lat, longitude: GPS_lon, altitude: GPS_alt, speed: GPS_speed }}); } - if(navigator.geolocation) CHASE_enabled = navigator.geolocation.watchPosition(positionUpdateHandle, positionUpdateError); - //CHASE_enabled = true; + CHASE_enabled = true; // hide the blue man if(currentPosition && currentPosition.marker) map.removeLayer(currentPosition.marker); @@ -693,6 +741,23 @@ $(window).ready(function() { // turning the switch on } else { e.removeClass('off').addClass('on'); + // Remove any planned recovery check. + $("#sw_recovery_planned").removeClass('on').addClass('off'); + } + }); + // Logic to switch the recovery Planned button + $("#sw_recovery_planned").click(function() { + var e = $(this); + + // turning the switch off + if(e.hasClass('on')) { + e.removeClass('on').addClass('off'); + + // turning the switch on + } else { + e.removeClass('off').addClass('on'); + // Set recovery OK to false. + $("#sw_recovery_ok").removeClass('on').addClass('off'); } }); // Logic to switch the use car position button @@ -713,11 +778,21 @@ $(window).ready(function() { callsign = $(this).val().trim(); offline.set('callsign', callsign); // put in localStorage CHASE_listenerSent = false; + $('#recovery_callsign').val(callsign); }); + $("#recovery_callsign").on('change keyup', function() { + callsign = $(this).val().trim(); + offline.set('callsign', callsign); // put in localStorage + CHASE_listenerSent = false; + $('#cc_callsign').val(callsign); + }); + + // load value from localStorage callsign = offline.get('callsign'); $('#cc_callsign').val(callsign); + $('#recovery_callsign').val(callsign); // settings page @@ -766,6 +841,18 @@ $(window).ready(function() { else focusVehicle(null, true); break; case "opt_imperial": + if (map_scale){ + map_scale.remove() + } + // we need to remove and add the zoom controls so they end up in the same position + zoom_controls.remove() + if (on) { + map_scale = L.control.scale({position:'bottomright', imperial:true, metric:false}).addTo(map); + } else { + map_scale = L.control.scale({position:'bottomright', imperial:false}).addTo(map); + } + zoom_controls.addTo(map); + case "opt_haxis_hours": refreshUI(); break; @@ -890,10 +977,7 @@ $(window).ready(function() { }); navigator.geolocation.getCurrentPosition(positionUpdateHandle); - // check for location update every 30sec - //setInterval(positionUpdateHandle, 30000); - // immediatelly check for position - //positionUpdateHandle(); + if(navigator.geolocation) navigator.geolocation.watchPosition(positionUpdateHandle, positionUpdateError); } // weather feature @@ -992,7 +1076,8 @@ $(window).ready(function() { $("header .search form").on('submit', function(e) { e.preventDefault(); - var text = $("header .search input[type='text']").val(); + var text = $("header .search input[type='text']").val().toUpperCase(); + $("header .search input[type='text']")[0].value = text; // update field in case it wasn't already uppercase if(text === wvar.query) return; @@ -1020,7 +1105,13 @@ function check_version(){ fetch(updateRequest) .then(function(response){ return response.json()}) .then(function(response){ - if (response['version'] != version) { + if (response['version'] != document.body.dataset.version) { + + caches.keys().then(function(names) { // wipe cache for fresh reload + for (let name of names) + caches.delete(name); + }); + window.clearInterval(update_check) reload_timer = window.setTimeout(update_site, response['refresh']*1000) reload_end_time = new Date().getTime() +response['refresh']*1000 @@ -1035,6 +1126,19 @@ function update_site(){ window.location.reload(true) } +function check_banner(){ + const bannerRequest = new Request("https://api.v2.sondehub.org/banner"); + fetch(bannerRequest) + .then(function(response){ return response.json()}) + .then(function(response){ + if (response['banner']) { + document.getElementsByTagName("header")[0].style.backgroundColor = "#ff6259" + document.getElementsByTagName("header")[0].style.borderColor = "#eb3d34" + document.getElementById("banner").innerText = response['banner'] + } + }) +} + function update_countdown(){ var date = new Date(0); time_remaining = (reload_end_time - new Date().getTime())/1000 @@ -1044,4 +1148,8 @@ function update_countdown(){ } check_version() -update_check = setInterval(check_version, 15 * 60 * 1000) \ No newline at end of file +check_banner() +update_check = setInterval(check_version, 15 * 60 * 1000) +load_hash(); +startAjax(); +load(); \ No newline at end of file diff --git a/js/chasecar.lib.js b/js/chasecar.lib.js index 4dd198c..1c470e2 100644 --- a/js/chasecar.lib.js +++ b/js/chasecar.lib.js @@ -24,7 +24,7 @@ ChaseCar.updatePosition = function(callsign, position) { var _doc = { "software_name": "SondeHub Tracker", - "software_version": "{VER}", + "software_version": document.body.dataset.version, "uploader_callsign": callsign, "uploader_position": [position.coords.latitude, position.coords.longitude, _position_alt], "uploader_antenna": "Mobile Station", @@ -45,8 +45,8 @@ ChaseCar.updatePosition = function(callsign, position) { ChaseCar.markRecovered = function(){ - // Distance limits reinstated 2021-12-04 - _run_checks = true; + // Remove range checks - 2025-01-24 + _range_check = false; _range_limit = 500000; // 200 km // Get the serial number to be marked recovered @@ -55,15 +55,15 @@ ChaseCar.markRecovered = function(){ _notes = $("#pr_notes").val().trim(); // Check it exists. - if(_serial.includes("chase") && _run_checks){ + if(_serial.includes("chase")){ $('#pr_last_report').text("Invalid sonde callsign."); return; } - _callsign = $("#cc_callsign").val().trim(); + _callsign = $("#recovery_callsign").val().trim(); if (_callsign == "" || _callsign == undefined || _callsign.length == 0) { - $('#pr_last_report').text("Enter a callsign in the chase-car section above!"); + $('#pr_last_report').text("Enter a callsign/name in the 'Recovered by' field!"); return; } @@ -109,7 +109,7 @@ ChaseCar.markRecovered = function(){ // Calculate the distance from the sonde _lookangles = calculate_lookangles(_chaser, _sonde); - if( (_lookangles.range > _range_limit ) && _run_checks){ + if( (_lookangles.range > _range_limit ) && _range_check){ $('#pr_last_report').text("Outside distance limit (500km)."); return; } @@ -120,28 +120,26 @@ ChaseCar.markRecovered = function(){ } } + // Logic to make recovery & planned mutually exclusive is in app.js + _recovered = $("#sw_recovery_ok").hasClass('on'); + _planned = $("#sw_recovery_planned").hasClass('on'); + var _doc = { "serial": _serial, "lat": _recov_lat, "lon": _recov_lon, "alt": 0.0, - "recovered": $("#sw_recovery_ok").hasClass('on'), + "recovered": _recovered, + "planned": _planned, "recovered_by": _callsign, "description": _notes }; - // Yes this is not the right way to do this... - // .. but it adds an extra bit of check. - var res = grecaptcha.getResponse(); - if (res == "" || res == undefined || res.length == 0) - { - $('#pr_last_report').text("Do Recaptcha first!"); - return; - } $('#pr_last_report').text("Submitting report..."); + $.ajax({ type: "PUT", url: ChaseCar.recovery_uri, diff --git a/js/plot_config.js b/js/plot_config.js index f40315a..e66bc6d 100644 --- a/js/plot_config.js +++ b/js/plot_config.js @@ -77,9 +77,9 @@ function updateLegend(pos) { if(outside && pij !== undefined) { if(!polyMarker) { - try {polyMarker = new L.Marker(vehicles[follow_vehicle].prediction_polyline.getLatLngs()[pij]).addTo(map);} catch (e) {}; + try {polyMarker = new L.Marker(vehicles[follow_vehicle].prediction_polyline[0].getLatLngs()[pij]).addTo(map);} catch (e) {}; } else { - try {polyMarker.setLatLng(vehicles[follow_vehicle].prediction_polyline.getLatLngs()[pij]);} catch (e) {}; + try {polyMarker.setLatLng(vehicles[follow_vehicle].prediction_polyline[0].getLatLngs()[pij]);} catch (e) {}; } } diff --git a/js/tracker.js b/js/sondehub.js similarity index 86% rename from js/tracker.js rename to js/sondehub.js index a49c01c..1296cc4 100644 --- a/js/tracker.js +++ b/js/sondehub.js @@ -52,10 +52,10 @@ var focusID = 0; var receiverCanvas = null; -var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "iMet-1", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "MTS01", "WxR-301D", "chase"]; +var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "PS20", "iMet-1", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "MTS01", "WxR-301D", "chase"]; var sondeCodes = { "07":"iMet-1", "11":"LMS6-403", "13":"RS92", "14":"RS92", "17":"DFM-09", "18":"DFM-06", "19":"MRZ-N1", "22":"RS-11G", "23":"RS41", "24":"RS41", "34":"iMet-4", "35":"iMS-100", "38":"WxR-301D", "41":"RS41", "42":"RS41", "52":"RS92-NGP", - "54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "65":"MTS01", "77":"M10", "82":"LMS6-1680", "84":"iMet-54" + "54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "64":"PS20", "65":"MTS01", "77":"M10", "82":"LMS6-1680", "84":"iMet-54" }; var unsupportedSondeCodes = { "15":"PAZA-12M", "16":"PAZA-22", "20":"MK3", "21":"1524LA LORAN-C/GL5000", "26":"SRS-C34", "27":"AVK-MRZ", "28":"AVK–AK2-02", "29":"MARZ2-2", "30":"RS2-80", "33":"GTS1-2/GFE(L)", "45":"CF-06", "58":"AVK-BAR", @@ -73,10 +73,8 @@ var manual_pan = false; // due to complaints it was not visible enough. - 2023-06-03 var car_index = 0; -var car_colors = ["blue", "red", "green", "yellow", "teal", "purple"]; -var balloon_index = 0; -var balloon_colors_name = ["red", "blue", "green", "purple", "orange", "cyan"]; -var balloon_colors = ["#f00", "blue", "green", "#c700e6", "#ff8a0f", "#0fffca"]; +var car_colors = ["#a6cee3", "#1f78b4", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6"]; +var balloon_colors = ["#d95f02", "#7570b3", "#e7298a", "#e6ab02", "#a6761d", "#666666", "blue", "magenta", "#ffb300", "rebeccapurple"]; var nyan_color_index = 0; var nyan_colors = ['nyan', 'nyan-coin', 'nyan-mon', 'nyan-pirate', 'nyan-cool', 'nyan-tothemax', 'nyan-pumpkin', 'nyan-afro', 'nyan-coin', 'nyan-mummy']; @@ -170,7 +168,8 @@ var v1manufacturers = { "LMS6-400": "Lockheed Martin", "LMS6-1680": "Lockheed Martin", "M10": "Meteomodem", - "M20": "Meteomodem" + "M20": "Meteomodem", + "PS20": "Meteomodem" } var globalKeys = { @@ -571,7 +570,7 @@ function makeQuad(x, y, zoom) { // map type list -var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { +var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap contributors' }); @@ -620,6 +619,16 @@ var getlost = L.tileLayer('https://live.getlost.com.au/{z}/{x}/{y}.jpg', { }); getlost.id = "GetLost"; +var highSight = L.tileLayer('https://api.highsight.dev/v1/tiles/{z}/{x}/{y}?key=ARX3jYxzWotJVz7ZgdDiMQI18tZJ5WCk', { + attribution: '© HighSight', + maxZoom: 9, + minZoom: 3, + crossOrigin: true + }); + +highSight.id = "HighSight"; + + var baseMaps = { "Mapnik": osm, "DarkMatter": dark_matter, @@ -627,62 +636,11 @@ var baseMaps = { "Terrain": stamen_terrain, "Voyager": cartodb_voyager, "OpenTopoMap": opentopomap, + "HighSight": highSight } var selectedLayer = "Mapnik"; -// Tile load analytics - 2023-01-09 -// This code allows us to get some understanding of total tile loads across all users. -var tile_loads = { - "Mapnik": 0, - "DarkMatter": 0, - "WorldImagery": 0, - "Terrain": 0, - "Voyager": 0, - "OpenTopoMap": 0, -} - -// Add handlers to eadh tileload event to simply increment a counter. -// We don't need any more data than this. -osm.on('tileload', function() { tile_loads["Mapnik"]++ }); -dark_matter.on('tileload', function() { tile_loads["DarkMatter"]++ }); -worldimagery.on('tileload', function() { tile_loads["WorldImagery"]++ }); -stamen_terrain.on('tileload', function() { tile_loads["Terrain"]++ }); -cartodb_voyager.on('tileload', function() { tile_loads["Voyager"]++ }); -opentopomap.on('tileload', function() { tile_loads["OpenTopoMap"]++ }); - - -var last_sent_tile_loads = {} - -setInterval(function(){ - - temp_tile_loads = Object.assign({},tile_loads); - - // Check if the tile load count has changed. - // Using JSON stringify is a bit of a hack, but appropriate for this kind of job. - if(JSON.stringify(last_sent_tile_loads) == JSON.stringify(temp_tile_loads)){ - // Tile loads havent changed, do nothing, - } else { - // Tile loads have changed. Update the store, and send the data. - last_sent_tile_loads = Object.assign({},tile_loads); - - // Send! - - $.ajax({ - type: "PUT", - url: "https://api.v2.sondehub.org/tiles/count", - contentType: "application/json; charset=utf-8", - dataType: "json", - data: JSON.stringify({'client': clientID, 'tile_loads': last_sent_tile_loads}), - }); - } - -}, 60000) - - - - - // set map if in memory var maplayer = offline.get("map") @@ -723,7 +681,7 @@ function throttle_events(event) { function sub_to_nearby_sondes(){ let bounds = map.getBounds().pad(1); // expand by one viewport - let zoomed_out = map.getZoom() <= 6; + let zoomed_out = map.getZoom() <= 8; const sub_logging = false; if (zoomed_out){ // If we are fairly zooomed out - only give the slow feed @@ -818,7 +776,6 @@ function clean_refresh(text, force, history_step) { wvar.mode = text; document.getElementById("timeperiod").value = text; - document.getElementById("timeperiod").disabled = true; position_id = 0; @@ -831,14 +788,13 @@ function clean_refresh(text, force, history_step) { } car_index = 0; - balloon_index = 0; nyan_color_index = 0; stopFollow(force); // add loading spinner in the vehicle list $('#main .empty').parent().remove(); - $("#main .portrait,#main .landscape").append( - '
' + + $(".scrollwrapper").append( + '
' + '
' ); listScroll.refresh(); @@ -861,20 +817,30 @@ function clean_refresh(text, force, history_step) { } function load() { + coords = offline.get("mc"); + if (coords == null) { + coords = [53.467511,-2.233894]; + } + + zoomLevel = offline.get("mz"); + if (zoomLevel == null) { + zoomLevel = 5; + } + //initialize map object map = new L.map(document.getElementById('map'), { - zoom: 5, + zoom: zoomLevel, zoomControl: false, zoomAnimationThreshold: 0, zoomAnimation: true, markerZoomAnimation: false, - center: [53.467511,-2.233894], + center: coords, layers: baseMaps[selectedLayer], worldCopyJump: true, preferCanvas: true, }); - map.setView([53.467511,-2.233894], 5, {animate: false}); + map.setView(coords, zoomLevel, {animate: false}); // fullscreen button map.addControl(new L.Control.Fullscreen({ position: 'bottomleft' })); @@ -902,10 +868,15 @@ function load() { L.control.status({ position: 'bottomright' }).addTo(map); // scale (would be better if integrated into attirbution bar) - L.control.scale({position:'bottomright', imperial:false}).addTo(map); + if (offline.get('opt_imperial')){ + map_scale = L.control.scale({position:'bottomright', imperial:true, metric:false}).addTo(map); + } else { + map_scale = L.control.scale({position:'bottomright', imperial:false}).addTo(map); + } + // zoom controls - new L.Control.Zoom({ position: 'bottomright' }).addTo(map); + zoom_controls = new L.Control.Zoom({ position: 'bottomright' }).addTo(map); // map selector layers = L.control.layers(baseMaps, null, {position: "topleft"}).addTo(map); @@ -975,8 +946,6 @@ function load() { L.control.predictionontrol({ position: 'topleft' }).addTo(map); - // update current position if we geolocation is available - if(currentPosition) updateCurrentPosition(currentPosition.lat, currentPosition.lon); //Receiver canvas receiverCanvas = new L.LayerGroup(); @@ -1015,6 +984,10 @@ function load() { lhash_update(); sidebar_update(); sub_to_nearby_sondes(); + + var latlng = map.getCenter(); + offline.set("mc", [roundNumber(latlng.lat, 5), roundNumber(latlng.lng, 5)]); + offline.set("mz", map.getZoom()); }); map.on('baselayerchange', function (e) { @@ -1050,6 +1023,7 @@ function load() { } } updateZoom(); + recoveryPopup(); }); map.on('movestart', function() { @@ -1070,7 +1044,7 @@ function load() { lhash_update(); }); - startAjax(); + //startAjax(); liveData(); }; @@ -1090,7 +1064,7 @@ function load() { var img = this._createImg(this.options['iconUrl']); var numdiv = document.createElement('div'); numdiv.setAttribute ( "class", "number" ); - numdiv.innerHTML = this.options['number'] || ''; + numdiv.innerText = this.options['number'] || ''; div.appendChild ( img ); div.appendChild ( numdiv ); this._setIconStyles(div, 'icon'); @@ -1136,9 +1110,7 @@ function load() { } }); - // if we there is enough screen space open aboutbox on startup - if(!is_mobile && !offline.get('opt_nowelcome') && $(window).width() > 900) $('.nav li.about').click(); - + }, 500); } @@ -1222,6 +1194,7 @@ function panToRecovery(rcallsign) { //if mobile close panel if (is_mobile) { $('.flatpage, #homebox').hide(); + lhash_update(true); } for (let i = 0; i < recoveries.length; i++) { if (recoveries[i].hasOwnProperty('serial')) { @@ -1362,6 +1335,11 @@ function habitat_data(jsondata, data_ages, current_timestamp, alternative) { } } + // Round frequency data + if(k === "frequency"){ + v = v.toFixed(3); + } + // Check if data is considered to be 'old' // Need to think about how to style old data. // Maybe make the text grey? @@ -1485,6 +1463,73 @@ function drawLOSPaths(vcallsign) { }); } +var recovery_popup = true; +function recoveryPopup(){ + // Conditions for showing this are: + // Zoom level >= 13 + // A sonde is in the map view area. (Use the subscribed sondes for this) + // If a user position is available, that position is within 500km of the sonde. + recovery_popup_limit = 500000; + + if( (map.getZoom() >= 13) && (clientTopic.length >= 2)){ + if(recovery_popup == true){ + // Logic to not show a popup if the centre of the map is > 500km from the viewer location, + _viewer_lat = parseFloat($('#cc_lat').text()) + _viewer_lon = parseFloat($('#cc_lon').text()) + + if ( (_viewer_lat != 0.0) && (_viewer_lon != 0.0) ){ + + _map_centre = { + 'lat':map.getCenter().lat, + 'lon':map.getCenter().lng, + 'alt':0.0 + }; + _viewer = { + 'lat': _viewer_lat, + 'lon': _viewer_lon, + 'alt': 0.0 + }; + + // Calculate the distance from the sonde + _lookangles = calculate_lookangles(_viewer, _map_centre); + + if(_lookangles.range > recovery_popup_limit){ + return; + } + } + + // If we're here, then we either don't have a valid viewer location, + // Or the viewer is within 500km. Show the popup. + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "block"; + } + return; + } + } + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "none"; + } + } + +function show_recoveries(){ + // Open the recovery reporting tab + if ($("#recoveriesbox").is(':visible') == false){ + $('.nav .recoveries').click(); + } + + // Hide the recovery notice + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "none"; + } + + // Don't show the recovery notice again. + recovery_popup = false; +} + + function focusVehicle(vcallsign, ignoreOpt) { if(!offline.get('opt_hilight_vehicle') && ignoreOpt === undefined) return; @@ -1616,9 +1661,9 @@ function formatDate(date,utc) { if(typeof utc != "undefined") { z = date.getTimezoneOffset() / -60; - return a+'-'+b+'-'+c+' '+e+':'+f+':'+g+" UTC"+((z<0)?"-":"+")+z; + return a+'-'+b+'-'+c+' '+e+':'+f+':'+g+" UTC"+((z<0)?"-":"+")+z; } else { - return a+'-'+b+'-'+c+' '+e+':'+f+':'+g; + return a+'-'+b+'-'+c+' '+e+':'+f+':'+g; } } @@ -1655,7 +1700,7 @@ function updateVehicleInfo(vcallsign, newPosition) { vehicle.marker.setLatLng(latlng); if(!!vehicle.marker.setCourse) { - if (vehicle.curr_position.gps_heading) { + if (vehicle.curr_position.gps_heading && vehicle.marker.rotated) { vehicle.marker.setCourse((vehicle.curr_position.gps_heading !== "") ? parseInt(vehicle.curr_position.gps_heading) : 90); } } @@ -1779,7 +1824,13 @@ function updateVehicleInfo(vcallsign, newPosition) { $('.landscape').append('
'); } - if (offline.get('opt_selective_sidebar')) { + + } else if(elm.attr('data-vcallsign') === undefined) { + elm.attr('data-vcallsign', vcallsign); + } + + if (offline.get('opt_selective_sidebar')) { + if (document.getElementById("pv"+vehicle.uuid) && document.getElementById("lv"+vehicle.uuid)){ if (map.getBounds().contains(vehicles[vcallsign].marker.getLatLng())) { document.getElementById("pv"+vehicle.uuid).style.display = "block"; document.getElementById("lv"+vehicle.uuid).style.display = "block"; @@ -1788,10 +1839,8 @@ function updateVehicleInfo(vcallsign, newPosition) { document.getElementById("lv"+vehicle.uuid).style.display = "none"; } } +} - } else if(elm.attr('data-vcallsign') === undefined) { - elm.attr('data-vcallsign', vcallsign); - } // decides how to display the horizonal speed var imp = offline.get('opt_imperial'), hrate_text; @@ -1802,23 +1851,7 @@ function updateVehicleInfo(vcallsign, newPosition) { hrate_text = imp ? (vehicle.horizontal_rate * 196.850394).toFixed(1) + ' ft/min' : vehicle.horizontal_rate.toFixed(1) + ' m/s'; } - var coords_text; - var ua = navigator.userAgent.toLowerCase(); - - // determine how to link the vehicle coordinates to a native app, if on a mobile device - if(ua.indexOf('iphone') > -1) { - coords_text = '' + - roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5) +'' + - ' '; - } else if(ua.indexOf('android') > -1) { - coords_text = '' + - roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5) +'' + - ' '; - } else { - coords_text = '' + - roundNumber(newPosition.gps_lat, 5) + ', ' + roundNumber(newPosition.gps_lon, 5) +'' + - ' '; - } + var coords_text = format_coordinates(newPosition.gps_lat, newPosition.gps_lon, vcallsign) + ' '; // format altitude strings var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(newPosition.gps_alt)) : parseInt(newPosition.gps_alt)).toLocaleString("us"); @@ -2035,6 +2068,7 @@ function skewTdelete () { $('#deleteSkewt').hide(); $("#skewtSerial").text("Select a Radiosonde from the list and click 'SkewT' to plot. Note that not all radiosonde types are supported."); box.hide(); + lhash_update(true); //$('.skewt').hide(); $("#skewt-plot").empty(); checkSize(); @@ -2064,6 +2098,7 @@ function skewTdraw (callsign) { $('.flatpage, #homebox').hide(); $('.skewt').show(); box.show().scrollTop(0); + lhash_update(true); checkSize(); }; @@ -2161,7 +2196,7 @@ function skewTdraw (callsign) { // Extract temperature datapoint if (entry.hasOwnProperty('temp')){ - if(parseFloat(entry.temp) > -270.0){ + if(parseFloat(entry.temp) > -250.0){ _temp = parseFloat(entry.temp); } else{ idx = idx + 1; @@ -2174,15 +2209,15 @@ function skewTdraw (callsign) { } + _dewp = null; + // Try and extract RH datapoint if (entry.hasOwnProperty('humidity')){ if(parseFloat(entry.humidity) >= 0.0){ _rh = parseFloat(entry.humidity); // Calculate the dewpoint _dewp = (243.04 * (Math.log(_rh / 100) + ((17.625 * _temp) / (243.04 + _temp))) / (17.625 - Math.log(_rh / 100) - ((17.625 * _temp) / (243.04 + _temp)))); - } else { - _dewp = -1000.0; - } + } } // Calculate movement @@ -2210,7 +2245,11 @@ function skewTdraw (callsign) { break; } - _new_skewt_data = {"press": _pressure, "hght": _new_pos.alt, "temp": _temp, "dwpt": _dewp, "wdir": _wdir, "wspd": _wspd}; + _new_skewt_data = {"press": _pressure, "hght": _new_pos.alt, "temp": _temp, "wdir": _wdir, "wspd": _wspd}; + + if (_dewp != null){ + _new_skewt_data.dwpt = _dewp + } skewt_data.push(_new_skewt_data); @@ -2228,6 +2267,7 @@ function skewTdraw (callsign) { $('.flatpage, #homebox').hide(); $('.skewt').show(); box.show().scrollTop(0); + lhash_update(true); checkSize(); }; @@ -2262,16 +2302,20 @@ function set_polyline_visibility(vcallsign, val) { if(vehicle.prediction_polyline) { if (val) { - map.addLayer(vehicle.prediction_polyline); + map.addLayer(vehicle.prediction_polyline[0]); + map.addLayer(vehicle.prediction_polyline[1]); } else { - map.removeLayer(vehicle.prediction_polyline); + map.removeLayer(vehicle.prediction_polyline[0]); + map.removeLayer(vehicle.prediction_polyline[1]); } } if(vehicle.prediction_launch_polyline) { if (val) { - map.addLayer(vehicle.prediction_launch_polyline); + map.addLayer(vehicle.prediction_launch_polyline[0]); + map.addLayer(vehicle.prediction_launch_polyline[1]); } else { - map.removeLayer(vehicle.prediction_launch_polyline); + map.removeLayer(vehicle.prediction_launch_polyline[0]); + map.removeLayer(vehicle.prediction_launch_polyline[1]); } } if(vehicle.prediction_target) { @@ -2294,7 +2338,8 @@ function set_polyline_visibility(vcallsign, val) { function removePrediction(vcallsign) { if(vehicles[vcallsign].prediction_polyline) { - map.removeLayer(vehicles[vcallsign].prediction_polyline); + map.removeLayer(vehicles[vcallsign].prediction_polyline[0]); + map.removeLayer(vehicles[vcallsign].prediction_polyline[1]); vehicles[vcallsign].prediction_polyline = null; } if(vehicles[vcallsign].prediction_target) { @@ -2323,19 +2368,48 @@ function drawLaunchPrediction(vcallsign) { vehicle.prediction_launch_path = line; - vehicle.prediction_launch_polyline = new L.Polyline(line, { + vehicle.prediction_launch_polyline = [new L.Polyline(line, { color: balloon_colors[vehicle.color_index], - opacity: 0.4, + opacity: 0.8, weight: 3, - })//.addTo(map); - - vehicle.prediction_launch_polyline.on('click', function (e) { + }), + new L.Polyline(line, { + color: "#000", + opacity: 0.1, + weight: 6, +}) +]//.addTo(map); + + vehicle.prediction_launch_polyline[0].on('click', function (e) { + mapInfoBox_handle_prediction_path(e); + }); + vehicle.prediction_launch_polyline[1].on('click', function (e) { mapInfoBox_handle_prediction_path(e); }); - vehicle.prediction_launch_polyline.path_length = path_length; + vehicle.prediction_launch_polyline[0].path_length = path_length; + vehicle.prediction_launch_polyline[1].path_length = path_length; } +// Takes in an SVG for a balloon, parachute, target, car, etc and sets a dynamic-color +// variable which that SVG can use to recolor any relevant elements. +// See balloon.svg, target.svg, etc for examples +var svg_cache = {} +function recolorSVG(svg_path, color) { + if (svg_cache[svg_path] == undefined){ + const xhr = new XMLHttpRequest(); + xhr.open('GET', svg_path, false); + xhr.send(); + + const parser = new DOMParser(); + const svgDocument = parser.parseFromString(xhr.responseText, 'image/svg+xml'); + svg_cache[svg_path] = svgDocument; + } + const svgDoc = svg_cache[svg_path] + svgDoc.documentElement.style.setProperty("--dynamic-color", color); + return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgDoc.documentElement.outerHTML); + } + function redrawPrediction(vcallsign) { var vehicle = vehicles[vcallsign]; var data = vehicle.prediction.data; @@ -2375,19 +2449,31 @@ function redrawPrediction(vcallsign) { vehicle.prediction_path = line; if(vehicle.prediction_polyline !== null) { - vehicle.prediction_polyline.setLatLngs(line); + vehicle.prediction_polyline[0].setLatLngs(line); + vehicle.prediction_polyline[1].setLatLngs(line); } else { - vehicle.prediction_polyline = new L.Polyline(line, { + vehicle.prediction_polyline = [new L.Polyline(line, { color: balloon_colors[vehicle.color_index], - opacity: 0.5, // Was 0.4 + opacity: 0.8, // Was 0.4 weight: 3, - })//.addTo(map); - vehicle.prediction_polyline.on('click', function (e) { + }), + new L.Polyline(line, { + color: "#000", + opacity: 0.1, // Was 0.4 + weight: 6, + }) + + ]//.addTo(map); + vehicle.prediction_polyline[0].on('click', function (e) { mapInfoBox_handle_prediction_path(e); }); + vehicle.prediction_polyline[1].on('click', function (e) { + mapInfoBox_handle_prediction_path(e); + }) } - vehicle.prediction_polyline.path_length = path_length; + vehicle.prediction_polyline[0].path_length = path_length; + vehicle.prediction_polyline[1].path_length = path_length; var image_src; @@ -2395,7 +2481,7 @@ function redrawPrediction(vcallsign) { if(vehicle.prediction_target) { vehicle.prediction_target.setLatLng(latlng); } else { - image_src = host_url + markers_url + "target-" + balloon_colors_name[vehicle.color_index] + ".png"; + image_src = recolorSVG(host_url + markers_url + "target.svg", balloon_colors[vehicle.color_index]); predictionIcon = new L.icon({ iconUrl: image_src, iconSize: [20,20], @@ -2415,13 +2501,26 @@ function redrawPrediction(vcallsign) { vehicle.prediction_target.pred_type = "Prediction Type: Float\n"; } else { if(vehicle.prediction.descending == 1){ - vehicle.prediction_target.pred_type = "Prediction Type: Standard \n" + - "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + vehicle.prediction_target.pred_type = "Prediction Type: Standard \n" + if (offline.get('opt_imperial')){ + vehicle.prediction_target.pred_type = vehicle.prediction_target.pred_type + "Descent Rate: " + (vehicle.prediction.descent_rate * 3.2808399).toFixed(1) + " ft/s \n"; + } else { + vehicle.prediction_target.pred_type = vehicle.prediction_target.pred_type + "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + } + } else { - vehicle.prediction_target.pred_type = "Prediction Type: Standard \n" + - "Ascent Rate: " + vehicle.prediction.ascent_rate.toFixed(1) + " m/s \n" + - "Burst Altitude: " + vehicle.prediction.burst_altitude.toFixed(0) + " m\n" + - "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + if (offline.get('opt_imperial')){ + vehicle.prediction_target.pred_type = "Prediction Type: Standard \n" + + "Ascent Rate: " + (vehicle.prediction.ascent_rate * 3.2808399).toFixed(1) + " ft/s \n" + + "Burst Altitude: " + (vehicle.prediction.burst_altitude * 3.2808399).toFixed(0) + " ft\n" + + "Descent Rate: " + (vehicle.prediction.descent_rate * 3.2808399).toFixed(1) + " ft/s \n"; + + } else { + vehicle.prediction_target.pred_type = "Prediction Type: Standard \n" + + "Ascent Rate: " + vehicle.prediction.ascent_rate.toFixed(1) + " m/s \n" + + "Burst Altitude: " + vehicle.prediction.burst_altitude.toFixed(0) + " m\n" + + "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + } } } @@ -2439,7 +2538,8 @@ function redrawPrediction(vcallsign) { vehicle.prediction_burst = new L.Marker(latlng_burst, { zIndexOffset: Z_SHADOW, icon: burstIcon, - }).addTo(map); + }); + vehicle.prediction_burst.pred_type = "Burst\n"; vehicle.prediction_burst.on('click', function (e) { mapInfoBox_handle_prediction(e); }); @@ -2449,13 +2549,25 @@ function redrawPrediction(vcallsign) { vehicle.prediction_burst.pred_type = "Prediction Type: Float\n"; } else { if(vehicle.prediction.descending == 1){ - vehicle.prediction_burst.pred_type = "Prediction Type: Standard \n" + - "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + vehicle.prediction_burst.pred_type = "Prediction Type: Standard \n" + if (offline.get('opt_imperial')){ + vehicle.prediction_burst.pred_type = vehicle.prediction_burst.pred_type + "Descent Rate: " + (vehicle.prediction.descent_rate * 3.2808399).toFixed(1) + " ft/s \n"; + } else { + vehicle.prediction_burst.pred_type = vehicle.prediction_burst.pred_type + "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + } } else { - vehicle.prediction_burst.pred_type = "Prediction Type: Standard \n" + + if (offline.get('opt_imperial')){ + vehicle.prediction_burst.pred_type = "Prediction Type: Standard \n" + + "Ascent Rate: " + (vehicle.prediction.ascent_rate * 3.2808399).toFixed(1) + " ft/s \n" + + "Burst Altitude: " + (vehicle.prediction.burst_altitude * 3.2808399).toFixed(0) + " ft\n" + + "Descent Rate: " + (vehicle.prediction.descent_rate * 3.2808399).toFixed(1) + " ft/s \n"; + } else { + vehicle.prediction_burst.pred_type = "Prediction Type: Standard \n" + "Ascent Rate: " + vehicle.prediction.ascent_rate.toFixed(1) + " m/s \n" + "Burst Altitude: " + vehicle.prediction.burst_altitude.toFixed(0) + " m\n" + "Descent Rate: " + vehicle.prediction.descent_rate.toFixed(1) + " m/s \n"; + + } } } } else { @@ -2684,51 +2796,68 @@ function mapInfoBox_handle_path_old(vehicle, id) { div = document.createElement('div'); html = "
"; - html += "
"+data.serial+" ("+data.datetime+")
"; + html += "
"; html += "
"; - html += "
 "+roundNumber(data.lat, 5) + ', ' + roundNumber(data.lon, 5)+"
"; + html += "
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(data.alt)) : parseInt(data.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); + text_alt += " " + ((imp) ? 'ft':'m'); - html += "
Altitude: "+text_alt+"
"; - html += "
Time: "+formatDate(stringToDateUTC(data.datetime))+"
"; + html += "
Altitude:
"; + html += "
Time:
"; var value = vehicle.path_length; - html += "
Distance: "; + html += "
Distance: "; if(offline.get('opt_imperial')) { - html += Math.round(value*0.000621371192) + " mi"; + var distance = Math.round(value*0.000621371192) + " mi"; } else { - html += Math.round(value/10)/100 + " km"; + var distance = Math.round(value/10)/100 + " km"; } html += "
"; - html += "
Duration: " + format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + "
"; + html += "
Duration:
"; html += "
"; if (data.hasOwnProperty("humidity")) { - html += "
Relative Humidity: " + data.humidity + " %
"; + html += "
Relative Humidity: %
"; }; if (data.hasOwnProperty("temp")) { - html += "
Temperature External: " + data.temp + "°C
"; + html += "
Temperature External: °C
"; }; if (data.hasOwnProperty("comment")) { - html += "
Comment: " + data.comment + "
"; + html += "
Comment:
"; }; html += "
"; html += "
" if (data.hasOwnProperty("uploader_callsign")) { - html += "
" + data.uploader_callsign + "
"; + html += "
"; }; + + div.innerHTML = html; + div.getElementsByClassName("old_path_serial")[0].textContent = data.serial + div.getElementsByClassName("old_path_time")[0].textContent = "("+data.datetime+")" + div.getElementsByClassName("old_path_coords")[0].innerHTML = format_coordinates(data.lat, data.lon, data.serial) + div.getElementsByClassName("old_path_alt")[0].textContent = text_alt + div.getElementsByClassName("old_path_time_short")[0].textContent = formatDate(stringToDateUTC(data.datetime)) + div.getElementsByClassName("old_path_time_distance")[0].textContent = distance + div.getElementsByClassName("old_path_duration")[0].textContent = format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + div.getElementsByClassName("old_path_humidity")[0].textContent = data.humidity + div.getElementsByClassName("old_path_temp")[0].textContent = data.temp + div.getElementsByClassName("old_path_comment")[0].textContent = data.comment + + if (data.hasOwnProperty("uploader_callsign")) { + div.getElementsByClassName("old_path_uploader_callsign")[0].textContent = data.uploader_callsign + } + mapInfoBox.setContent(div); mapInfoBox.openOn(map); @@ -2759,87 +2888,90 @@ function mapInfoBox_handle_path_new(data, vehicle, date) { div = document.createElement('div'); html = "
"; - html += "
"+data.serial+" ("+date+")
"; + html += "
"; html += "
"; - html += "
 "+roundNumber(data.lat, 5) + ', ' + roundNumber(data.lon, 5)+"
"; + html += "
 
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(data.alt)) : parseInt(data.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); + text_alt += " " + ((imp) ? 'ft':'m'); - html += "
Altitude: "+text_alt+"
"; - html += "
Time: "+formatDate(stringToDateUTC(date))+"
"; + html += "
Altitude:
"; + html += "
Time:
"; var value = vehicle.path_length; - html += "
Distance: "; + html += "
Distance: "; if(offline.get('opt_imperial')) { - html += Math.round(value*0.000621371192) + " mi"; + var distance = Math.round(value*0.000621371192) + " mi"; } else { - html += Math.round(value/10)/100 + " km"; + var distance = Math.round(value/10)/100 + " km"; } - html += "
"; - html += "
Duration: " + format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + "
"; + html += "
"; + html += "
Duration:
"; - html += "
"; + html += "
"; - if (data.hasOwnProperty("batt")) { - html += "
Battery Voltage: " + data.batt + " V
"; - }; - if (data.hasOwnProperty("tx_frequency")) { - html += "
TX Frequency: " + data.tx_frequency + " MHz
"; - } else if (data.hasOwnProperty("frequency")) { - html += "
Frequency: " + data.frequency + " MHz
"; - }; - if (data.hasOwnProperty("humidity")) { - html += "
Relative Humidity: " + data.humidity + " %
"; - }; - if (data.hasOwnProperty("manufacturer")) { - html += "
Manufacturer: " + data.manufacturer + "
"; - }; - if (data.hasOwnProperty("pressure")) { - html += "
Pressure: " + data.pressure + " Pa
"; - }; - if (data.hasOwnProperty("sats")) { - html += "
Satellites: " + data.sats + "
"; - }; - if (data.hasOwnProperty("temp")) { - html += "
Temperature External: " + data.temp + "°C
"; - }; - if (data.hasOwnProperty("subtype")) { - html += "
Sonde Type: " + data.subtype + "
"; - } else if (data.hasOwnProperty("type")) { - html += "
Sonde Type: " + data.type + "
"; - }; + function prop(parent, field_name, description, unit="", fixed=undefined){ + if (data.hasOwnProperty(field_name)){ + var div = document.createElement("div") + var b = document.createElement("b") + b.innerText = description + ": " + var span = document.createElement("span") + var value = data[field_name]; + if (fixed){ + value = value.toFixed(fixed) + } + span.textContent = value + " " + unit + div.appendChild(b) + div.appendChild(span) + parent.appendChild(div) + return true + } + return false + } + var xdata_fields = document.createElement("div"); if (data.hasOwnProperty("xdata")) { html += "
"; html += "
" - html += "
XDATA: " + data.xdata + "
"; + html += "
"; if (data.hasOwnProperty("pressure")) { xdata_pressure = data.pressure; } else { xdata_pressure = 1100.0; } var tempXDATA = parseXDATA(data.xdata, xdata_pressure); + for (let field in tempXDATA) { if (tempXDATA.hasOwnProperty(field)) { + var xdiv = document.createElement("div"); + var xb = document.createElement("b"); + var xs = document.createElement("span"); + xdiv.appendChild(xb) + xdiv.appendChild(xs) + xdata_fields.appendChild(xdiv) if (field == "xdata_instrument") { - html += "
XDATA Instrument: " + tempXDATA.xdata_instrument.join(', ') + "
"; + xb.textContent = "XDATA Instrument: " + xs.textContent = tempXDATA.xdata_instrument.join(', ') } else { if (globalKeys.hasOwnProperty(field)) { if (globalSuffixes.hasOwnProperty(field)) { - html += "
" + globalKeys[field] + ": " + tempXDATA[field] + globalSuffixes[field] + "
"; + xb.textContent = globalKeys[field] + ": " + xs.textContent = tempXDATA[field] + globalSuffixes[field] } else { - html += "
" + globalKeys[field] + ": " + tempXDATA[field] + "
"; + xb.textContent = globalKeys[field] + ": " + xs.textContent = tempXDATA[field] } } else { if (globalSuffixes.hasOwnProperty(field)) { - html += "
" + guess_name(field) + ": " + tempXDATA[field] + globalSuffixes[field] + "
"; + xb.textContent = guess_name(field) + ": " + xs.textContent = tempXDATA[field] + globalSuffixes[field] } else { - html += "
" + guess_name(field) + ": " + tempXDATA[field] + "
"; + xb.textContent = guess_name(field) + ": " + xs.textContent = tempXDATA[field] } } } @@ -2870,11 +3002,48 @@ function mapInfoBox_handle_path_new(data, vehicle, date) { callsign_list.push(_new_call); // catch cases where there are no fields } - callsign_list = callsign_list.join("
"); - - html += callsign_list + "
"; + callsign_list = callsign_list.join("\n"); + callsign_span = document.createElement("span") + callsign_span.innerText = callsign_list div.innerHTML = html; + div.appendChild(callsign_span) + + div.getElementsByClassName("new_path_serial")[0].textContent = data.serial + div.getElementsByClassName("new_path_time")[0].textContent = "("+date+")" + + div.getElementsByClassName("new_path_coords")[0].innerHTML = format_coordinates(data.lat, data.lon, data.serial); // for compat we are generating safeish url in format_coordinates + + div.getElementsByClassName("new_path_alt")[0].textContent = text_alt + div.getElementsByClassName("new_path_time_short")[0].textContent = formatDate(stringToDateUTC(date)) + + + div.getElementsByClassName("new_path_time_distance")[0].textContent = distance + + div.getElementsByClassName("new_path_duration")[0].textContent = format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + + + var prop_parent = div.getElementsByClassName("new_path_props")[0] + prop(prop_parent,"batt", "Battery Voltage", "V"); + + if (!prop(prop_parent,"tx_frequency", "TX Frequency", "MHz", 3)){ + prop(prop_parent,"frequency", "Frequency", "MHz", 3); + } + + + prop(prop_parent,"humidity", "Relative Humidity","%", 1); + prop(prop_parent,"manufacturer", "Manufacturer"); + prop(prop_parent,"pressure", "Pressure", "Pa", 1); + prop(prop_parent,"sats", "Satellites"); + prop(prop_parent,"temp", "Temperature External", "°C", 1); + + if (!prop(prop_parent,"subtype", "Sonde Type")){ + prop(prop_parent,"type", "Sonde Type") + } + prop(prop_parent,"xdata", "XDATA"); + if (data.hasOwnProperty("xdata")) { + div.getElementsByClassName("new_path_xdata_prop")[0].appendChild(xdata_fields) + } mapInfoBox.setContent(div); mapInfoBox.openOn(map); @@ -2895,20 +3064,7 @@ function mapInfoBox_handle_prediction(event) { altitude = Math.round(data.alt) + " m"; } - var coords_text; - var ua = navigator.userAgent.toLowerCase(); - - // determine how to link the vehicle coordinates to a native app, if on a mobile device - if(ua.indexOf('iphone') > -1) { - coords_text = '' + - roundNumber(data.lat, 5) + ', ' + roundNumber(data.lon, 5) + ''; - } else if(ua.indexOf('android') > -1) { - coords_text = '' + - roundNumber(data.lat, 5) + ', ' + roundNumber(data.lon, 5) +''; - } else { - coords_text = '' + - roundNumber(data.lat, 5) + ', ' + roundNumber(data.lon, 5) +''; - } + var coords_text = format_coordinates(data.lat, data.lon, "Prediction"); mapInfoBox.setContent("
" +
                         formatDate(new Date(parseInt(data.time) * 1000), true) + "\n\n" +
@@ -2989,10 +3145,10 @@ var marker_rotate_setup = function(marker, image_src) {
     else {
         marker.iconImg = new Image();
         icon_cache[image_src] = marker.iconImg;
-        marker.iconImg.onload = function() {
+        marker.iconImg.addEventListener("load", function() {
             if(!marker.rotated) marker.setCourse(90);
             marker.setLatLng(marker.getLatLng());
-        };
+        })
         marker.iconImg.src = image_src;
     }
 };
@@ -3023,9 +3179,12 @@ function addPosition(position) {
         var horizon_circle_title = null;
         var subhorizon_circle_title = null;
         if(vcallsign.search(/(chase)/i) != -1) {
+            if (offline.get("opt_hide_chase") ){
+                return
+            }
             vehicle_type = "car";
             color_index = car_index++ % car_colors.length;
-            image_src = host_url + markers_url + "car-" + car_colors[color_index] + ".png";
+            image_src = recolorSVG(host_url + markers_url + "car.svg", car_colors[color_index]);
             image_src_size = [55,25];
             image_src_offset = [0,-25];
 
@@ -3066,37 +3225,19 @@ function addPosition(position) {
                     weight: 3,
                 })
             ];
-        }
-        else if(vcallsign == "XX") {
-            vehicle_type = "xmark";
-            image_src = host_url + markers_url + "balloon-xmark.png";
-            image_src_size = [48,38];
-            image_src_offset = [0,-38];
-
-            xmarkIcon = new L.icon({
-                iconUrl: image_src,
-                iconSize: image_src_size,
-                iconAnchor: [24, 18],
-            });
-
-            marker = new L.Marker(point, {
-                icon: xmarkIcon,
-                title: vcallsign,
-                zIndexOffset: Z_CAR,
-            });
-
-            marker.addTo(map);
         } else {
             vehicle_type = "balloon";
-            color_index = balloon_index++ % balloon_colors.length;
+            let colorHash = 0;
+            for (let i = 0; i < vcallsign.length; i++){
+                colorHash += vcallsign.charCodeAt(i);
+            }
+            color_index = colorHash % balloon_colors.length;
 
             if(wvar.nena){
                 // All the balloon are red.
                 color_index = 0;
             }
-
-            image_src = host_url + markers_url + "balloon-" +
-                        ((vcallsign == "PIE") ? "rpi" : balloon_colors_name[color_index]) + ".png";
+            image_src = recolorSVG(host_url + markers_url + "balloon.svg", balloon_colors[color_index]);
             image_src_size = [46,84];
             image_src_offset = [-35,-46];
 
@@ -3141,7 +3282,7 @@ function addPosition(position) {
             };
 
             marker.shadow = marker_shadow;
-            marker.balloonColor = (vcallsign == "PIE") ? "rpi" : balloon_colors_name[color_index];
+            marker.balloonColor = balloon_colors[color_index];
             marker.mode = 'balloon';
             marker.setMode = function(mode) {
                 if(this.mode == mode) return;
@@ -3154,9 +3295,9 @@ function addPosition(position) {
                     map.removeLayer(vehicle.subhorizon_circle);
                     map.removeLayer(vehicle.horizon_circle_title);
                     map.removeLayer(vehicle.subhorizon_circle_title);
-
+                    img_src = recolorSVG(host_url + markers_url + "payload.svg", this.balloonColor);
                     img = new L.icon ({
-                        iconUrl: host_url + markers_url + "payload-" + this.balloonColor + ".png",
+                        iconUrl: img_src,
                         iconSize: [17,18],
                         iconAnchor: [8,14],
                         tooltipAnchor: [0,-20],
@@ -3172,15 +3313,17 @@ function addPosition(position) {
                     }
 
                     if(mode == "parachute") {
+                        img_src = recolorSVG(host_url + markers_url + "parachute.svg", this.balloonColor);
                         img = new L.icon ({
-                            iconUrl: host_url + markers_url + "parachute-" + this.balloonColor + ".png",
+                            iconUrl: img_src,
                             iconSize: [46,84],
                             tooltipAnchor: [0,-98],
                             iconAnchor: [23,90],
                         });
                     } else {
+                        img_src = recolorSVG(host_url + markers_url + "balloon.svg", this.balloonColor);
                         img = new L.icon ({
-                            iconUrl: host_url + markers_url + "balloon-" + this.balloonColor + ".png",
+                            iconUrl: img_src,
                             iconSize: [46,84],
                             tooltipAnchor: [0,-98],
                             iconAnchor: [23,90],
@@ -3235,7 +3378,8 @@ function addPosition(position) {
 
             horizon_circle_title = new L.Marker(point, {
                 icon: horizon_circle_title_icon,
-                interactive: false,
+                interactive: true,
+                title: "Line-of-right (radio) horizon of the payload"
             });
 
             if (offline.get("opt_hide_horizon")) {
@@ -3267,7 +3411,8 @@ function addPosition(position) {
 
             subhorizon_circle_title = new L.Marker(point, {
                 icon: subhorizon_circle_title_icon,
-                interactive: false,
+                interactive: true,
+                title: "Payload is greater than 5 degrees above the horizon within this circle, which indicates it should be fairly easily receivable."
             });
 
             if (offline.get("opt_hide_horizon")) {
@@ -3288,15 +3433,20 @@ function addPosition(position) {
                     color: balloon_colors[color_index],
                     opacity: 1,
                     weight: 3,
+                }),
+                new L.Polyline(point, {
+                    color: "#fff",
+                    opacity: 0.6,
+                    weight: 6,
                 })
             ];
         }
 
         if (!offline.get("opt_hide_titles")) {
             if (vehicle_type == "car") {
-                title = marker.bindTooltip(vcallsign, {direction: 'center', permanent: 'true', className: 'serialtooltip'});
+                title = marker.bindTooltip(vcallsign, {direction: 'center', permanent: 'false', className: 'serialtooltip'});
             } else {
-                title = marker.bindTooltip((tempTitle), {direction: 'center', permanent: 'true', className: 'serialtooltip'});
+                title = marker.bindTooltip((tempTitle), {direction: 'center', permanent: 'false', className: 'serialtooltip'});
             }
         } else {
             title = null;
@@ -3399,11 +3549,13 @@ function addPosition(position) {
         vehicle_info.kill = function() {
             $(".vehicle"+vehicle_info.uuid).remove();
             potentialobjects = [marker, marker_shadow, landing_marker, horizon_circle, horizon_circle_title, subhorizon_circle, subhorizon_circle_title, polyline];
-            if (map.hasLayer(vehicle_info["prediction_polyline"])) { 
-                map.removeLayer(vehicle_info["prediction_polyline"]);
+            if (vehicle_info["prediction_polyline"] && map.hasLayer(vehicle_info["prediction_polyline"][0])) { 
+                map.removeLayer(vehicle_info["prediction_polyline"][0]);
+                map.removeLayer(vehicle_info["prediction_polyline"][1]);
             }
-            if (map.hasLayer(vehicle_info["prediction_launch_polyline"])) { 
-                map.removeLayer(vehicle_info["prediction_launch_polyline"]);
+            if (vehicle_info["prediction_launch_polyline"] && map.hasLayer(vehicle_info["prediction_launch_polyline"][0])) { 
+                map.removeLayer(vehicle_info["prediction_launch_polyline"][0]);
+                map.removeLayer(vehicle_info["prediction_launch_polyline"][1]);
             }
             if (map.hasLayer(vehicle_info["prediction_target"])) { 
                 map.removeLayer(vehicle_info["prediction_target"]);
@@ -3528,7 +3680,7 @@ function addPosition(position) {
         };
 
         // if car doesn't report heading, we calculate it from the last position
-        if(vehicle.num_positions > 1 && vehicle.vehicle_type == 'car' && 'gps_heading' in position && position.gps_heading === "") {
+        if(vehicle.num_positions > 1 && vehicle.vehicle_type == 'car') {
 
             // Source
             var startLat = toRadians(vehicle.curr_position.gps_lat);
@@ -3883,24 +4035,26 @@ function refresh() {
     data: data_str,
     dataType: "json",
     success: function(data, textStatus) {
-        if (wvar.query != null && JSON.stringify(data).indexOf(wvar.query) == -1) {
-            refreshSingle(wvar.query);
-        } else {
-            response = formatData(data, false);
-            update(response, true);   
-            $("#stTimer").attr("data-timestamp", response.fetch_timestamp);
+      var checkLoadedTimer = setInterval(() => {
+        if (map){
+          if (wvar.query != null && JSON.stringify(data).indexOf(wvar.query) == -1) {
+              refreshSingle(wvar.query);
+          } else {
+              response = formatData(data, false);
+              update(response, true);   
+              $("#stTimer").attr("data-timestamp", response.fetch_timestamp);
+          }
+          $("#stText").text("");
+          clearInterval(checkLoadedTimer)
         }
-        $("#stText").text("");
+      }, 50)
     },
     error: function() {
         $("#stText").text("poll error |");
-        document.getElementById("timeperiod").disabled = false;
         ajax_inprogress = false;
     },
     complete: function(request, textStatus) {
-        if (!ajax_inprogress_single) {
-            document.getElementById("timeperiod").disabled = false;
-        }
+
         clientActive = true;
         //console.log("WebSockets - Resuming Websockets updates after poll.")
         clearTimeout(periodical);
@@ -3909,6 +4063,7 @@ function refresh() {
   });
 }
 
+
 live_data_buffer = {positions:{position:[]}}
 function liveData() {
     client.onConnectionLost = onConnectionLost;
@@ -4052,7 +4207,10 @@ function liveData() {
 
 // Interval to read in the live data buffer and update the page.
 setInterval(function(){
-    update(live_data_buffer);
+    if(map){
+      update(live_data_buffer);
+    }
+    
     live_data_buffer.positions.position=[];
 }, 500)
 
@@ -4077,20 +4235,23 @@ function refreshSingle(serial) {
       url: data_url,
       dataType: "json",
       success: function(data, textStatus) {
-        response = formatData(data, false);
-        update(response, true);
-        singleRecovery(serial);
-        $("#stText").text("");
+        var checkLoadedTimer = setInterval(() => {
+          if (map){
+            response = formatData(data, false);
+            update(response, true);
+            singleRecovery(serial);
+            $("#stText").text("");
+            clearInterval(checkLoadedTimer);
+          }
+        }, 50)
       },
       error: function() {
         $("#stText").text("error |");
         ajax_inprogress_single = false;
-        document.getElementById("timeperiod").disabled = false;
       },
       complete: function(request, textStatus) {
           clearTimeout(periodical_focus);
           ajax_inprogress_single = false;
-          document.getElementById("timeperiod").disabled = false;
       }
     });
 }
@@ -4112,12 +4273,17 @@ function refreshSingleNew(serial) {
     ajax_inprogress_single_new = true;
   
     var data_str = "duration=3d&serial=" + serial;
-  
+
+    var xhr = new XMLHttpRequest();
+
     ajax_positions_single_new = $.ajax({
       type: "GET",
       url: newdata_url,
       data: data_str,
       dataType: "json",
+      xhr: function() {
+        return xhr;
+      },
       success: function(data, textStatus) {
         response = formatData(data, false);
         update(response, true);
@@ -4128,6 +4294,20 @@ function refreshSingleNew(serial) {
       complete: function(request, textStatus) {
           clearTimeout(periodical_focus_new);
           ajax_inprogress_single_new = false;
+          // check if we have been redirected for history - if not we can also fetch that if it exists
+          if (xhr.responseURL.includes(newdata_url)){
+            $.ajax({
+                type: "GET",
+                url: "https://sondehub-history.s3.amazonaws.com/serial/"+ encodeURIComponent(serial) +".json.gz",
+                dataType: "json",
+                success: function(data, textStatus) {
+                  response = formatData(data, false);
+                  update(response, true);
+                },
+                error: function() {
+                }
+            })
+          }
       }
     });
 }
@@ -4167,7 +4347,12 @@ function refreshReceivers() {
             data: data_str,
             dataType: "json",
             success: function(response, textStatus) {
-                updateReceivers(response, single=false);
+              var checkLoadedTimer = setInterval(() => {
+                if (map){
+                  updateReceivers(response, single=false);
+                  clearInterval(checkLoadedTimer);
+                }
+              }, 50);
             },
             complete: function(request, textStatus) {
                 if (!offline.get("opt_hide_chase")) {
@@ -4220,7 +4405,9 @@ function singleRecovery(serial) {
         data: datastr,
         dataType: "json",
         success: function(response, textStatus) {
-            updateRecoveries(response);
+            if(!offline.get('opt_hide_recoveries')) {
+                updateRecoveries(response, true);
+            }
         }
     });
 
@@ -4233,10 +4420,15 @@ function refreshRecoveries() {
         url: recovered_sondes_url,
         dataType: "json",
         success: function(response, textStatus) {
-            updateRecoveryPane(response);
-            if(!offline.get('opt_hide_recoveries')) {
-                updateRecoveries(response);
+          var checkLoadedTimer = setInterval(() => {
+            if (map){
+              updateRecoveryPane(response);
+              if(!offline.get('opt_hide_recoveries')) {
+                  updateRecoveries(response);
+              }
+              clearInterval(checkLoadedTimer);
             }
+          }, 50);
         },
         error: function() {
             updateRecoveryPane([]);
@@ -4263,45 +4455,6 @@ function refreshRecoveryStats() {
 
 var ajax_predictions = null;
 
-// function refreshPredictions() {
-//     if(ajax_inprogress) {
-//       clearTimeout(periodical_predictions);
-//       periodical_predictions = setTimeout(refreshPredictions, 1000);
-//       return;
-//     }
-
-//     ajax_predictions = $.ajax({
-//         type: "GET",
-//         url: predictions_url + encodeURIComponent(wvar.query),
-//         data: "",
-//         dataType: "json",
-//         success: function(response, textStatus) {
-//             updatePredictions(response);
-//         },
-//         error: function() {
-//         },
-//         complete: function(request, textStatus) {
-//             clearTimeout(periodical_predictions);
-//             periodical_predictions = setTimeout(refreshPredictions, 60 * 1000);
-//         }
-//     });
-
-//     var data_str = "duration=" + wvar.mode + "&vehicles=" + encodeURIComponent(wvar.query);
-
-//     ajax_predictions = $.ajax({
-//         type: "GET",
-//         url: launch_predictions_url,
-//         data: data_str,
-//         dataType: "json",
-//         success: function(response, textStatus) {
-//             updateLaunchPredictions(response);
-//         },
-//         error: function() {
-//         },
-//         complete: function(request, textStatus) {
-//         }
-//     });
-// }
 
 var periodical, periodical_focus, periodical_focus_new, periodical_receivers, periodical_listeners, periodical_recoveries;
 var periodical_predictions = null;
@@ -4309,8 +4462,6 @@ var timer_seconds = 5;
 
 function startAjax() {
 
-    document.getElementById("timeperiod").disabled = true;
-
     // prevent insane clicks to start numerous requests
     clearTimeout(periodical);
     clearTimeout(periodical_focus);
@@ -4368,14 +4519,17 @@ function updateCurrentPosition(lat, lon) {
     } else {
       currentPosition.lat = lat;
       currentPosition.lon = lon;
-      currentPosition.marker.addTo(map);
-      currentPosition.marker.setLatLng(latlng);
+      if (!CHASE_enabled){
+        currentPosition.marker.addTo(map);
+        currentPosition.marker.setLatLng(latlng);
+      }
     }
 }
 
 function updateReceiverMarker(receiver) {
   var latlng = new L.LatLng(receiver.lat, receiver.lon);
 
+
   // init a marker if the receiver doesn't already have one
   if(!receiver.marker) {
 
@@ -4412,8 +4566,10 @@ function updateReceiverMarker(receiver) {
     }
     
     receiver.marker.bindPopup(receiver.infobox);
-
-    receiverCanvas.addLayer(receiver.marker);
+    if (!offline.get("opt_hide_receivers") ){
+        receiverCanvas.addLayer(receiver.marker);
+    }
+   
   } else {
     receiver.marker.setLatLng(latlng);
     receiver.infobox = new L.popup({ autoClose: false, closeOnClick: false }).setContent(receiver.description);
@@ -4453,6 +4609,7 @@ function updateChase(r) {
                         dataTempEntry.gps_alt = last.uploader_position[2];
                         dataTempEntry.gps_lat = last.uploader_position[0];
                         dataTempEntry.gps_lon = last.uploader_position[1];
+                        dataTempEntry.gps_heading = 90;
                         var date = new Date(last.ts)
                         var userTimezoneOffset = date.getTimezoneOffset() * 60000;
                         var time = new Date(date.getTime() - userTimezoneOffset).toISOString();
@@ -4586,7 +4743,7 @@ function updateReceivers(r, single) {
         i = 0;
         for(; i < receivers.length;) {
             var e = receivers[i];
-            if(e.fresh) {
+            if(e.fresh && !offline.get("opt_hide_receivers")) {
                 e.fresh = false;
                 i++;
             }
@@ -4615,6 +4772,13 @@ function updateRecoveryMarker(recovery) {
         _recovery_icon = host_url + markers_url + "payload-not-recovered.png";
       }
 
+    // Override icon if 'is planned' field exists and is true
+        if(recovery.hasOwnProperty('planned')){
+            if(recovery.planned == true){
+                _recovery_icon = host_url + markers_url + "payload-recovery-planned.png";
+            }
+      }
+
       recoveryIcon = new L.icon({
         iconUrl: _recovery_icon,
         iconSize: [17, 19],
@@ -4634,23 +4798,36 @@ function updateRecoveryMarker(recovery) {
 
       div = document.createElement('div');
 
+      _recovered_text = recovery.recovered ? " Recovered" : " Not Recovered";
+
+      // Override text is planned field exists and is true
+      if(recovery.hasOwnProperty('planned')){
+        if(recovery.planned == true){
+            _recovered_text = " Recovery Planned";
+        }
+      }
+
       html = "
"; - html += "
"+recovery.serial+(recovery.recovered ? " Recovered" : " Not Recovered")+"
"; + html += "
"; html += "
"; - html += "
 "+roundNumber(recovery.lat, 5) + ', ' + roundNumber(recovery.lon, 5)+"
"; + html += "
 "+format_coordinates(recovery.lat, recovery.lon, recovery.serial)+"
"; - var imp = offline.get('opt_imperial'); - var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(recovery.alt)) : parseInt(recovery.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); - - html += "
Time: "+formatDate(stringToDateUTC(recovery.datetime))+"
"; - html += "
Reported by: "+recovery.recovered_by+"
"; - html += "
Notes: "+$('
').text(recovery.description).html()+"
"; - html += "
Flight Path: "+recovery.serial+"
"; + html += "
Time: 
"; + html += "
Reported by: 
"; + html += "
Notes: 
"; + html += "
Flight Path: 
"; html += "
"; div.innerHTML = html; + div.getElementsByClassName("recovery_text")[0].textContent = recovery.serial+_recovered_text + div.getElementsByClassName("recovery_time")[0].textContent = formatDate(stringToDateUTC(recovery.datetime)) + div.getElementsByClassName("recovery_by")[0].textContent = recovery.recovered_by + div.getElementsByClassName("recovery_desc")[0].textContent = recovery.description + div.getElementsByClassName("recovery_path")[0].textContent = recovery.serial + div.getElementsByClassName("recovery_path")[0].onclick = function(){ + showRecoveredMap(recovery.serial) + } recovery.infobox.setContent(div); @@ -4659,8 +4836,7 @@ function updateRecoveryMarker(recovery) { } } - function updateRecoveries(r) { - + function updateRecoveries(r, single=false) { if(!r) return; ls_recoveries = true; @@ -4669,41 +4845,39 @@ function updateRecoveryMarker(recovery) { var i = 0, ii = r.length; for(; i < ii; i++) { - var date = Date.parse(r[i].datetime); - if (((dateNow - date) / 86400000) < 3) { - var lat = parseFloat(r[i].lat); - var lon = parseFloat(r[i].lon); - - if(lat < -90 || lat > 90 || lon < -180 || lon > 180) continue; - - var r_index = $.inArray(r[i].serial, recovery_names); - - if(r_index == -1) { - recovery_names.push(r[i].serial); - r_index = recovery_names.length - 1; - recoveries[r_index] = {marker: null, infobox: null}; - } - - var recovery = recoveries[r_index]; - recovery.serial = r[i].serial; - recovery.lat = lat; - recovery.lon = lon; - recovery.recovered_by = r[i].recovered_by; - recovery.alt = parseFloat(r[i].alt); - recovery.recovered = r[i].recovered; - recovery.description = r[i].description; - recovery.datetime = r[i].datetime; - recovery.fresh = true; - - updateRecoveryMarker(recovery); - } + var lat = parseFloat(r[i].lat); + var lon = parseFloat(r[i].lon); + + if(lat < -90 || lat > 90 || lon < -180 || lon > 180) continue; + + var r_index = $.inArray(r[i].serial, recovery_names); + + if(r_index == -1) { + recovery_names.push(r[i].serial); + r_index = recovery_names.length - 1; + recoveries[r_index] = {marker: null, infobox: null}; + } + + var recovery = recoveries[r_index]; + recovery.serial = r[i].serial; + recovery.lat = lat; + recovery.lon = lon; + recovery.recovered_by = r[i].recovered_by; + recovery.alt = parseFloat(r[i].alt); + recovery.recovered = r[i].recovered; + recovery.description = r[i].description; + recovery.datetime = r[i].datetime; + recovery.planned = r[i].planned; + recovery.fresh = true; + updateRecoveryMarker(recovery); + } // clear old recovery markers i = 0; for(; i < recoveries.length;) { var e = recoveries[i]; - if(e.fresh) { + if(e.fresh || single) { e.fresh = false; i++; } @@ -4747,9 +4921,18 @@ function updateRecoveryPane(r){ recoveries[r_index] = {marker: null, infobox: null}; } + _recovered_text = r[i].recovered ? " Recovered by " : " Not Recovered by "; + + // Override text is planned field exists and is true + if(r[i].hasOwnProperty('planned')){ + if(r[i].planned == true){ + _recovered_text = " Recovery Planned by "; + } + } + html += "
"; - html += "
"+r[i].serial+(r[i].recovered ? " Recovered by " : " Not Recovered by ")+r[i].recovered_by+"
"; - html += "
 "+roundNumber(lat, 5) + ', ' + roundNumber(lon, 5)+"
"; + html += "
"+r[i].serial+(_recovered_text)+r[i].recovered_by+"
"; + html += "
 "+format_coordinates(lat, lon, r[i].serial)+"
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(alt)) : parseInt(alt)).toLocaleString("us"); @@ -4831,23 +5014,26 @@ function updatePredictions(r) { if(vcallsign == "XX") continue; if(vehicles.hasOwnProperty(vcallsign)) { - var vehicle = vehicles[vcallsign]; - - if(vehicle.marker.mode == "landed") { - removePrediction(vcallsign); - continue; + var vehicle = vehicles[vcallsign]; + if(vehicle.prediction && vehicle.prediction.time == r[i].time) continue; + vehicle.prediction = r[i]; + vehicle.prediction.data = $.parseJSON(r[i].data); + + // Figure out local ground level. + if(vehicle.prediction.data.length >= 2){ + vehicle.local_ground_asl = vehicle.prediction.data[vehicle.prediction.data.length-1]['alt']; + } else { + vehicle.local_ground_asl = 0; } - if(vehicle.prediction && vehicle.prediction.time == r[i].time) { - continue; - } - vehicle.prediction = r[i]; - if(parseInt(vehicle.prediction.landed) === 0) { - vehicle.prediction.data = $.parseJSON(r[i].data); + // Only draw prediction if the last known position of the payload was > 100m above local ground level. + if( (vehicle.curr_position['gps_alt']-vehicle.local_ground_asl) > 100){ redrawPrediction(vcallsign); } else { - removePrediction(vcallsign); + continue; } + + } } } @@ -4876,6 +5062,8 @@ function showHorizonRings(){ if(vehicles[vcallsign].vehicle_type == "balloon"){ map.addLayer(vehicles[vcallsign].horizon_circle); map.addLayer(vehicles[vcallsign].subhorizon_circle); + vehicles[vcallsign].horizon_circle.fire("move") + vehicles[vcallsign].subhorizon_circle.fire("move") map.addLayer(vehicles[vcallsign].horizon_circle_title); map.addLayer(vehicles[vcallsign].subhorizon_circle_title); } @@ -4893,7 +5081,7 @@ function hideTitles(){ function showTitles(){ for(var vcallsign in vehicles) { if(vehicles[vcallsign].vehicle_type == "balloon" || vehicles[vcallsign].vehicle_type == "car"){ - vehicles[vcallsign].title = vehicles[vcallsign].marker.bindTooltip(vehicles[vcallsign]["marker"]["options"]["title"], {direction: 'center', permanent: 'true', className: 'serialtooltip'}); + vehicles[vcallsign].title = vehicles[vcallsign].marker.bindTooltip(vehicles[vcallsign]["marker"]["options"]["title"], {direction: 'center', permanent: 'false', className: 'serialtooltip'}); } } } @@ -5013,7 +5201,7 @@ function update(response, none) { }, }; - + document.getElementById("spinner")?.remove(); ctx_init.run(ctx_init); sub_to_nearby_sondes(); } diff --git a/js/station.js b/js/station.js index 09cade0..6ad8857 100644 --- a/js/station.js +++ b/js/station.js @@ -174,12 +174,22 @@ function drawHistorical (data, station) { } } - var popup = L.popup(); + var popup = new L.popup({ autoClose: false, closeOnClick: false }).setContent(serial); + + if (!recovered) { + var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "white", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + } else { + var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "grey", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + } + + marker.bindPopup(popup); + + div = document.createElement('div'); html = "
"; html += "
"+serial+" ("+time+")
"; html += "
"; - html += "
 "+roundNumber(landing.lat, 5) + ', ' + roundNumber(landing.lon, 5)+"
"; + html += "
Last Position: "+format_coordinates(landing.lat, landing.lon, serial)+"
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(landing.alt)) : parseInt(landing.alt)).toLocaleString("us"); @@ -187,40 +197,60 @@ function drawHistorical (data, station) { html += "
Altitude: "+text_alt+"
"; html += "
Time: "+formatDate(stringToDateUTC(time))+"
"; - - if (landing.hasOwnProperty("type")) { - html += "
Sonde Type: " + landing.type + "
"; - }; + html += "
Sonde Type: Unknown
" html += "
"; - if (recovered) { - html += "
"+(recovery_info.recovered ? "Recovered by " : "Not Recovered by ")+recovery_info.recovered_by+"
"; - html += "
Recovery time: "+formatDate(stringToDateUTC(recovery_info.datetime))+"
"; - html += "
Recovery location: "+recovery_info.position[1]+", "+recovery_info.position[0] + "
"; - html += "
Recovery notes: "+recovery_info.description+"
"; - - html += "
"; - } + html += "
"; + html += "
"; + html += "
Reported at: 
"; + html += "
Reported by: 
"; + html += "
Notes: 
"; + html += "
"; + html += "
"; - html += "
Show Full Flight Path: " + serial + "
"; + html += "
Show Full Flight Path: 
"; html += "
"; html += "
" + html += "
" + html += "
"; - if (landing.hasOwnProperty("uploader_callsign")) { - html += "
Last received by: " + landing.uploader_callsign.toLowerCase() + "
"; + div.innerHTML = html; + + + div.getElementsByClassName("recovery_path")[0].textContent = serial + div.getElementsByClassName("recovery_path")[0].onclick = function(){ + showRecoveredMap(serial) + } + + if (landing.hasOwnProperty("type")) { + div.getElementsByClassName("landing_sonde_type")[0].textContent = landing.type; }; - popup.setContent(html); + if (landing.hasOwnProperty("uploader_callsign")) { + div.getElementsByClassName("landing_uploader_callsign")[0].textContent = "Last received by: " + landing.uploader_callsign.toLowerCase(); + }; - if (!recovered) { - var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "white", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + if (recovered) { + _recovered_text = recovery_info.recovered ? " Recovered" : "Not Recovered"; + + // Override text is planned field exists and is true + if(recovery_info.hasOwnProperty('planned')){ + if(recovery_info.planned == true){ + _recovered_text = " Recovery Planned"; + } + } + div.getElementsByClassName("recovery_text")[0].textContent = recovery_info.serial + _recovered_text; + div.getElementsByClassName("recovery_time")[0].textContent = formatDate(stringToDateUTC(recovery_info.datetime)); + div.getElementsByClassName("recovery_by")[0].textContent = recovery_info.recovered_by; + div.getElementsByClassName("recovery_desc")[0].textContent = recovery_info.description; } else { - var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "grey", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + div.getElementsByClassName("recovery_section")[0].style.display = "none"; } - marker.bindPopup(popup); + popup.setContent(div); + marker.addTo(map); marker.bringToBack(); @@ -602,26 +632,26 @@ function launchSitePredictions(times, station, properties, marker, id) { var date = new Date(); var time = times[i].split(":"); if (time[0] != 0) { - date.setDate(date.getDate() + (7 + time[0] - date.getDay()) % 7); + date.setUTCDate(date.getUTCDate() + (7 + time[0] - date.getUTCDay()) % 7); } date.setUTCHours(time[1]); date.setUTCMinutes(time[2]); - date.setSeconds(0); - date.setMilliseconds(0); + date.setUTCSeconds(0); + date.setUTCMilliseconds(0); // launch time 45 minutes before target time - date.setMinutes( date.getMinutes() - 45 ); + date.setUTCMinutes( date.getUTCMinutes() - 45 ); while (date < now) { if (time[0] == 0) { - date.setDate(date.getDate() + 1); + date.setUTCDate(date.getUTCDate() + 1); } else { - date.setDate(date.getDate() + 7); + date.setUTCDate(date.getUTCDate() + 7); } } if (day > 0) { if (time[0] == 0) { - date.setDate(date.getDate() + day); + date.setUTCDate(date.getUTCDate() + day); } else { - date.setDate(date.getDate() + (7*day)); + date.setUTCDate(date.getUTCDate() + (7*day)); } } if (count < maxCount) { @@ -738,15 +768,24 @@ function plotPrediction (data, dates, marker, properties) { icon: new L.NumberedDivIcon({number: dates.indexOf(data.request.launch_datetime)+1}) }).addTo(map); + var coords_text = format_coordinates(landingPoint.latitude, landingLongitude, "Prediction"); + var landingTime = new Date(landingPoint.datetime); if (properties[3] != "" && properties[4] != "") { var landingTooltip = "Time: " + landingTime.toLocaleString() + "
Model Dataset: " + data.request.dataset + + "
Prediction: " + coords_text + "
Model Assumptions:
- " + data.request.ascent_rate + "m/s ascent
- " + data.request.burst_altitude + "m burst altitude (" + properties[3] + " samples)
- " + data.request.descent_rate + "m/s descent (" + properties[4] + " samples)"; } else { var landingTooltip = "Time: " + landingTime.toLocaleString() + "
Model Dataset: " + data.request.dataset + + "
Prediction:" + coords_text + "
Model Assumptions:
- " + data.request.ascent_rate + "m/s ascent
- " + data.request.burst_altitude + "m burst altitude
- " + data.request.descent_rate + "m/s descent"; } - plot.landingMarker.bindTooltip(landingTooltip, {offset: [13,-28]}); + + plot.landingMarker.bindPopup(landingTooltip, { autoClose: false, closeOnClick: false }) + .on('click', function (e) { + this.openPopup(); + }); + } function showPrediction(url) { @@ -917,6 +956,7 @@ function generateLaunchSites() { } popupContent += "
Know when this site launches? Contribute here"; + popupContent += "
Site Code: " + key; // Generate view historical button popupContent += "
"; diff --git a/service-worker.js b/service-worker.js deleted file mode 100644 index fc7d456..0000000 --- a/service-worker.js +++ /dev/null @@ -1,73 +0,0 @@ -self.addEventListener('install', function(event) { - event.waitUntil( - caches.open("{VER}").then(function(cache) { - return cache.addAll( - [ - '/css/mobile.css', - '/css/leaflet.css', - '/css/leaflet.fullscreen.css', - '/js/leaflet.js', - '/js/Leaflet.fullscreen.min.js', - '/js/L.Terminator.js', - '/js/mobile.js', - '/js/rbush.js', - '/js/leaflet.canvas-markers.js', - '/js/pwa.js', - '/js/init_plot.js', - '/img/markers/antenna-green.png', - '/img/markers/balloon-blue.png', - '/img/markers/balloon-cyan.png', - '/img/markers/balloon-green.png', - '/img/markers/balloon-orange.png', - '/img/markers/balloon-purple.png', - '/img/markers/balloon-red.png', - '/img/markers/balloon-yellow.png', - '/img/markers/car-blue.png', - '/img/markers/car-green.png', - '/img/markers/car-red.png', - '/img/markers/car-yellow.png', - '/img/markers/parachute-yellow.png', - '/img/markers/parachute-blue.png', - '/img/markers/parachute-cyan.png', - '/img/markers/parachute-green.png', - '/img/markers/parachute-orange.png', - '/img/markers/parachute-purple.png', - '/img/markers/parachute-red.png', - '/img/markers/payload-blue.png', - '/img/markers/payload-cyan.png', - '/img/markers/payload-green.png', - '/img/markers/payload-not-recovered.png', - '/img/markers/payload-orange.png', - '/img/markers/payload-purple.png', - '/img/markers/payload-recovered.png', - '/img/markers/payload-red.png', - '/img/markers/payload-yellow.png', - '/img/markers/target-blue.png', - '/img/markers/target-cyan.png', - '/img/markers/target-green.png', - '/img/markers/target-orange.png', - '/img/markers/target-purple.png', - '/img/markers/target-red.png', - '/img/markers/target-yellow.png', - '/img/markers/shadow.png', - '/img/markers/balloon-pop.png', - '/img/hab-spinner.gif', - '/img/marker-you.gif', - '/img/sondehub_logo.gif', - '/favicon.ico', - '/font/HabitatFont.woff', - '/font/Roboto-regular.woff', - '/index.html' - ] - ); - }) - ); -}); - -self.addEventListener('fetch', function (event) { - event.respondWith( - caches.match(event.request).then(function (response) { - return response || fetch(event.request); - }), - ); -}); \ No newline at end of file diff --git a/service-worker.template.js b/service-worker.template.js new file mode 100644 index 0000000..82e6ce6 --- /dev/null +++ b/service-worker.template.js @@ -0,0 +1,62 @@ +self.addEventListener('install', function(event) { + event.waitUntil( + caches.open("{VER}").then(function(cache) { + return cache.addAll( + [ + '/css/base.css', + '/css/skeleton.css', + '/css/layout.css', + '/css/habitat-font.css', + '/css/main.css', + '/css/leaflet.css', + '/css/leaflet.fullscreen.css', + '/css/skewt.css', + '/js/skewt.js', + '/js/aws-sdk-2.1029.0.min.js', + '/js/leaflet.js', + '/js/Leaflet.fullscreen.min.js', + '/js/L.Terminator.js', + '/js/L.TileLayer.NoGap.js', + '/js/paho-mqtt.js', + '/js/jquery-1.12.4-min.js', + '/js/iscroll.js', + '/js/chasecar.lib.js', + '/js/sondehub.js', + '/js/app.js', + '/js/colour-map.js', + '/js/xdata.js', + '/js/station.js', + '/js/format.js', + '/js/rbush.js', + '/js/pwa.js', + '/js/_jquery.flot.js', + '/js/plot_config.js', + '/img/markers/balloon.svg', + '/img/markers/car.svg', + '/img/markers/parachute.svg', + '/img/markers/payload.svg', + '/img/markers/payload-not-recovered.png', + '/img/markers/payload-recovered.png', + '/img/markers/target.svg', + '/img/markers/shadow.png', + '/img/markers/balloon-pop.png', + '/img/hab-spinner.gif', + '/img/sondehub_au.png', + '/favicon.ico', + '/font/HabitatFont.woff', + '/font/Roboto-regular.woff', + '/index.html', + '/' + ] + ); + }) + ); +}); + +self.addEventListener('fetch', function (event) { + event.respondWith( + caches.match(event.request).then(function (response) { + return response || fetch(event.request); + }), + ); +}); \ No newline at end of file diff --git a/tools/pngout.exe b/tools/pngout.exe deleted file mode 100755 index c72d567..0000000 Binary files a/tools/pngout.exe and /dev/null differ diff --git a/tools/yuicompressor-2.4.8.jar b/tools/yuicompressor-2.4.8.jar deleted file mode 100755 index a1cf0a0..0000000 Binary files a/tools/yuicompressor-2.4.8.jar and /dev/null differ