Merge develop branch into master for release 0.5.0

master
Keith Irwin 2017-03-15 06:30:36 -04:00
commit 564dc54666
No known key found for this signature in database
GPG Key ID: 378933C743E2BBC0
29 changed files with 541 additions and 2477 deletions

View File

@ -1,11 +1,11 @@
# Tracman
###### v 0.4.3
###### v 0.5.0
node.js application to display a map with user's location.
## Installation
```sh
$ git clone https://github.com/Tracman-org/Server.git && (cd server && exec npm install)
$ git clone https://github.com/Tracman-org/Server.git && (cd Server && exec npm install)
```
## Running
@ -14,8 +14,19 @@ $ git clone https://github.com/Tracman-org/Server.git && (cd server && exec npm
$ npm start
```
## Contributing
Tracman will be updated according to [this branching model](http://nvie.com/posts/a-successful-git-branching-model).
## Changelog
#### v0.5.0
* Updated libraries
* Fixed recognition of attached clients [#34](https://github.com/Tracman-org/Server/issues/21)
* Moved socket.io code to own file.
* Many minor fixes
#### v0.4.3
* Fixed memory store [#21](https://github.com/Tracman-org/Server/issues/21)

View File

@ -1,4 +1,6 @@
var passport = require('passport'),
'use strict';
const passport = require('passport'),
slug = require('slug'),
crypto = require('crypto'),
secret = require('./secrets.js'),
@ -30,7 +32,7 @@ passport.use(new GoogleStrategy({
// User not found
else /* create user */ {
var user = new User();
user = new User();
user.googleID = profile.id;
user.name = profile.displayName;
user.email = profile.emails[0].value;
@ -42,7 +44,7 @@ passport.use(new GoogleStrategy({
// user.isPro = false;
// user.isAdmin = false;
var cbc = 2;
var successMessage, failMessage
var successMessage, failMessage;
// Generate slug
(function checkSlug(s,cb) {

View File

@ -1,4 +1,6 @@
var secret = require('./secrets.js');
'use strict';
const secret = require('./secrets.js');
var throwErr = function(req,err){
console.log('middleware.js:5 '+typeof err);

View File

@ -1,6 +1,8 @@
var mongoose = require('mongoose');
'use strict';
module.exports = mongoose.model('User', {
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {type:String, required:true},
email: String,
slug: {type:String, required:true, unique:true},
@ -29,3 +31,5 @@ module.exports = mongoose.model('User', {
},
sk32: {type:String, required:true, unique:true}
});
module.exports = mongoose.model('User', userSchema);

View File

@ -1,4 +1,6 @@
var router = require('express').Router(),
'use strict';
const router = require('express').Router(),
mw = require('../middleware.js'),
User = require('../models/user.js');

View File

@ -1,4 +1,6 @@
var router = require('express').Router(),
'use strict';
const router = require('express').Router(),
passport = require('passport');
router.get('/login', function(req,res){

View File

@ -1,13 +1,21 @@
var router = require('express').Router(),
mw = require('../middleware.js'),
secret = require('../secrets.js'),
User = require('../models/user.js');
'use strict';
const slug = require('slug'),
mw = require('../middleware.js'),
User = require('../models/user.js'),
router = require('express').Router();
// Shortcut to favicon.ico
router.get('/favicon.ico', function(req,res){
res.redirect('/static/img/icon/by/16-32-48.ico');
});
// Index route
router.route('/')
.get(function(req,res,next){
.get(function(req,res,next){
// Logged in
if (req.session.passport&&req.session.passport.user) {
if ( req.session.passport && req.session.passport.user ){
// Get user
User.findById(req.session.passport.user, function(err, user){
if (err){ mw.throwErr(req,err); }
@ -21,9 +29,7 @@ router.route('/')
success: req.flash('succcess')[0]
});
}
});
// Not logged in
}
// Not logged in
@ -36,4 +42,55 @@ router.route('/')
});
// Settings
router.route('/settings')
// Get settings form
.get(mw.ensureAuth, function(req,res,next){
User.findById(req.session.passport.user, function(err,user){
if (err){ console.log('Error finding settings for user:',err); mw.throwErr(req,err); }
res.render('settings.html', {user:user});
});
// Set new settings
}).post(mw.ensureAuth, function(req,res,next){
User.findByIdAndUpdate(req.session.passport.user, {$set:{
name: req.body.name,
slug: slug(req.body.slug),
email: req.body.email,
settings: {
units: req.body.units,
defaultMap: req.body.map,
defaultZoom: req.body.zoom,
showSpeed: (req.body.showSpeed)?true:false,
showAlt: (req.body.showAlt)?true:false,
showStreetview: (req.body.showStreet)?true:false
}
}}, function(err, user){
if (err) { console.log('Error updating user settings:',err); mw.throwErr(req,err); }
else { req.flash('success', 'Settings updated. '); }
res.redirect('/map#');
});
})
// Delete user account
.delete(mw.ensureAuth, function(req,res,next){
User.findByIdAndRemove( req.session.passport.user,
function(err) {
if (err) {
console.log('Error deleting user:',err);
mw.throwErr(req,err);
} else {
req.flash('success', 'Your account has been deleted. ');
res.redirect('/');
}
}
);
});
router.route('/help')
.get(mw.ensureAuth, function(req,res){
res.render('help.html', {user:req.session.passport.user});
});
module.exports = router;

View File

@ -1,7 +1,8 @@
var router = require('express').Router(),
'use strict';
const router = require('express').Router(),
mw = require('../middleware.js'),
secret = require('../secrets.js'),
slug = require('slug'),
secrets = require('../secrets.js'),
User = require('../models/user.js');
// Show map
@ -44,8 +45,8 @@ router.get('/:slug?', function(req,res,next){
} else {
if (user && !mapuser) { mapuser = user; }
res.render('map.html', {
api: secret.mapAPI,
mapuser: mapuser,
mapApi: secrets.googleMapsAPI,
user: user,
noFooter: '1',
noHeader: (req.query.noheader)?req.query.noheader.match(/\d/)[0]:'',
@ -56,49 +57,4 @@ router.get('/:slug?', function(req,res,next){
});
router.post('/:slug?', mw.ensureAuth, function(req,res,next){
// Set new user settings
User.findByIdAndUpdate(req.session.passport.user, {$set:{name: req.body.name,
slug: slug(req.body.slug),
email: req.body.email,
settings: {
units: req.body.units,
defaultMap: req.body.map,
defaultZoom: req.body.zoom,
showSpeed: (req.body.showSpeed)?true:false,
showAlt: (req.body.showAlt)?true:false,
showStreetview: (req.body.showStreet)?true:false
}
}}, function(err, user){
if (err) { console.log('Error updating user settings:',err); mw.throwErr(req,err); }
else { req.flash('success', 'Settings updated. '); }
res.redirect('/map#');
});
});
router.delete('/:slug?', mw.ensureAuth, function(req,res,next){
// Delete user account
User.findByIdAndRemove(
req.session.passport.user,
function(err) {
if (err) {
console.log('Error deleting user:',err);
mw.throwErr(req,err);
} else {
req.flash('success', 'Your account has been deleted. ');
res.sendStatus(200);
}
}
)
});
// Redirect /id/ to /slug/
router.get('/id/:id', function(req,res,next){
User.findById(req.params.id, function(err, user){
if (err){ mw.throwErr(req,err); }
if (!user){ next(); }
else { res.redirect('/map/'+user.slug+((req.url.indexOf('?')<0)?'':('?'+req.url.split('?')[1]))); }
});
});
module.exports = router;

View File

@ -1,4 +1,6 @@
var router = require('express').Router(),
'use strict';
const router = require('express').Router(),
mw = require('../middleware.js'),
slug = require('slug'),
User = require('../models/user.js');

118
config/sockets.js Normal file
View File

@ -0,0 +1,118 @@
'use strict';
// Imports
const User = require('./models/user.js');
// Check for tracking clients
function checkForUsers(io, user) {
//console.log(`Checking for clients receiving updates for ${user}`);
// Checks if any sockets are getting updates for this user
//TODO: Use Object.values() after upgrading to node v7
if (Object.keys(io.sockets.connected).map( function(id){
return io.sockets.connected[id];
}).some( function(socket){
return socket.gets==user;
})) {
//console.log(`Activating updates for ${user}.`);
io.to(user).emit('activate','true');
} else {
//console.log(`Deactivating updates for ${user}.`);
io.to(user).emit('activate', 'false');
}
}
module.exports = {
checkForUsers: checkForUsers,
init: function(io){
io.on('connection', function(socket) {
//console.log(`${socket.id} connected.`);
// Log
//socket.on('log', function(text){
//console.log(`LOG: ${text}`);
//});
// This socket can set location (app)
socket.on('can-set', function(userId){
//console.log(`${socket.id} can set updates for ${userId}.`);
socket.join(userId, function(){
//console.log(`${socket.id} joined ${userId}`);
});
checkForUsers( io, userId );
});
// This socket can receive location (map)
socket.on('can-get', function(userId){
socket.gets = userId;
//console.log(`${socket.id} can get updates for ${userId}.`);
socket.join(userId, function(){
//console.log(`${socket.id} joined ${userId}`);
socket.to(userId).emit('activate', 'true');
});
});
// Set location
socket.on('set', function(loc){
//console.log(`${socket.id} set location for ${loc.usr}`);
loc.time = Date.now();
// Check for sk32 token
if (!loc.tok) { console.log('!loc.tok for loc:',loc) }
else {
// Get loc.usr
User.findById(loc.usr, function(err, user) {
if (err) { console.log('Error finding user:',err); }
if (!user) { console.log('User not found for loc:',loc); }
else {
// Confirm sk32 token
if (loc.tok!=user.sk32) { console.log('loc.tok!=user.sk32 || ',loc.tok,'!=',user.sk32); }
else {
// Broadcast location
io.to(loc.usr).emit('get', loc);
//console.log(`Broadcasting ${loc.lat}, ${loc.lon} to ${loc.usr}`);
// Save in db as last seen
user.last = {
lat: parseFloat(loc.lat),
lon: parseFloat(loc.lon),
dir: parseFloat(loc.dir||0),
spd: parseFloat(loc.spd||0),
time: loc.time
};
user.save(function(err) {
if (err) { console.log('Error saving user last location:'+loc.user+'\n'+err); }
});
}
}
});
}
});
// Shutdown (check for remaining clients)
socket.on('disconnect', function(reason){
//console.log(`${socket.id} disconnected because of a ${reason}.`);
// Check if client was receiving updates
if (socket.gets){
//console.log(`${socket.id} left ${socket.gets}`);
checkForUsers( io, socket.gets );
}
});
// Log errors
socket.on('error', function(err){
console.log('Socket error! ',err);
});
});
}
};

48
manifest.webmanifest Normal file
View File

@ -0,0 +1,48 @@
{
"name": "Tracman",
"short_name": "Tracman",
"author": "Keith Irwin",
"description": "GPS tracking service",
"homepage_url": "https://github.com/Tracman-org/Server",
"theme_color": "#222222",
"background_color": "#080808",
"display": "standalone",
"icons": [{
"src": "static/img/icon/by/228.png",
"sizes": "228x228",
"type": "image/png"
}, {
"src": "static/img/icon/by/152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "static/img/icon/by/128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "static/img/icon/by/72.png",
"sizes": "72x72",
"type": "image/png"
}, {
"src": "static/img/icon/by/57.png",
"sizes": "57x57",
"type": "image/png"
}, {
"src": "static/img/icon/by/48.png",
"sizes": "48x48",
"type": "image/png"
}, {
"src": "static/img/icon/by/32.png",
"sizes": "32x32",
"type": "image/png"
}, {
"src": "static/img/icon/by/16.png",
"sizes": "16x16",
"type": "image/png"
}, {
"src": "static/img/icon/by/16-32-48.ico",
"sizes": "16x16 32x32 48x48",
"type": "image/ico"
}]
}

View File

@ -1,18 +1,18 @@
{
"name": "tracman",
"version": "0.4.3",
"version": "0.5.0",
"description": "Tracks user's GPS location",
"main": "server.js",
"dependencies": {
"body-parser": "^1.15.0",
"body-parser": "^1.17.1",
"connect-flash": "^0.1.1",
"cookie-parser": "^1.4.1",
"cookie-session": "^2.0.0-alpha.1",
"express": "^4.13.3",
"express": "^4.15.2",
"kerberos": "0.0.17",
"moment": "^2.12.0",
"mongodb": "^2.1.4",
"mongoose": "^4.3.5",
"mongoose": "^4.9.0",
"node-jose": "^0.8.0",
"nunjucks": "^2.3.0",
"passport": "^0.3.2",
@ -37,12 +37,7 @@
"test": "mocha test.js",
"start": "node server.js",
"dev": "nodemon server.js",
"deploy": "ssh -t khp 'rsync -aP --delete --exclude-from /srv/tracman/.gitignore --exclude .git kptow:/srv/c9/tracman/ /srv/tracman && sudo systemctl reload-or-restart tracman'",
"drydeploy": "rsync --dry-run -vaP --delete --exclude-from /srv/c9/tracman/.gitignore --exclude .git /srv/c9/tracman/ khp:/srv/tracman",
"revert": "ssh -t khp 'rsync -aP --delete --exclude-from /srv/tracman/.gitignore --exclude .git /srv/tracman/ kptow:/srv/c9/tracman && sudo systemctl reload-or-restart tracman'",
"dryrevert": "rsync --dry-run -vaP --delete --exclude-from /srv/c9/tracman/.gitignore --exclude .git khp:/srv/tracman/ /srv/c9/tracman",
"log": "ssh -t khp 'journalctl -u tracman'",
"restart": "ssh -t khp 'sudo systemctl reload-or-restart tracman'"
"update": "sudo n stable && sudo npm update --save && sudo npm prune"
},
"repository": {
"type": "git",

108
server.js Normal file → Executable file
View File

@ -1,4 +1,7 @@
/* IMPORTS */ { var
'use strict';
/* IMPORTS */
const
express = require('express'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
@ -11,8 +14,9 @@
User = require('./config/models/user.js'),
app = express(),
http = require('http').Server(app),
io = require('socket.io')(http);
}
io = require('socket.io')(http),
sockets = require('./config/sockets.js');
/* SETUP */ {
/* Database */ mongoose.connect(secret.mongoSetup, {
@ -59,6 +63,9 @@
}
/* Routes */ {
app.get('/favicon.ico', function(req,res){
res.redirect('/static/img/icon/by/16-32-48.ico');
});
app.use('/',
require('./config/routes/index.js'),
require('./config/routes/auth.js'),
@ -103,91 +110,14 @@
}
}
/* Sockets */ {
sockets.init(io);
}
}
/* RUNTIME */ {
// Check for tracking users
function checkForUsers(room) {
if (room) {
io.to('app-'+room).emit('activate',
(io.of("/").adapter.rooms[room])?'true':'false'
);
} else {
User.find({}, function(err, users){
if (err) { console.log('Sockets error finding all users in all rooms: '+err); }
users.forEach( function(user){
checkForUsers(user.id);
});
});
}
}
// Websockets
io.on('connection', function(sock) {
// Set room to map user ID
sock.on('room', function(room) {
sock.join(room);
if (room.slice(0,4)!='app-'){
// External user
User.findById({_id:room}, function(err, user) {
if (err) { console.log('Sockets error finding tracked user of room '+room+'\n'+err); }
if (user) {
io.to('app-'+room).emit('activate','true'); }
});
} else { // Sets location
checkForUsers(room.slice(4));
}
});
// Recieving beacon
sock.on('app', function(loc){
loc.time = Date.now();
// Check for sk32 token
if (loc.tok) {
// Get loc.usr
User.findById(loc.usr, function(err, user) {
if (err) { console.log('Error finding user:',err); }
if (!user) { console.log('User not found'); }
else {
// Confirm sk32 token
if (loc.tok!=user.sk32) { console.log('loc.tok!=user.sk32 || ',loc.tok,'!=',user.sk32); }
else {
// Broadcast location to spectators
io.to(loc.usr).emit('trac', loc);
// Echo broadcast to transmittors
io.to('app-'+loc.usr).emit('trac', loc);
// Save in db as last seen
user.last = {
lat: parseFloat(loc.lat),
lon: parseFloat(loc.lon),
dir: parseFloat(loc.dir||0),
spd: parseFloat(loc.spd||0),
time: loc.time
};
user.save(function(err) {
if (err) { console.log('Error saving user last location:'+loc.user+'\n'+err); }
});
}
}
});
}
});
// Shutdown (check for users)
sock.onclose = function(reason){
var closedroom = Object.keys(
sock.adapter.sids[sock.id]).slice(1)[0];
setTimeout(function() {
checkForUsers(closedroom);
}, 3000);
Object.getPrototypeOf(this).onclose.call(this,reason);
};
});
// Listen
http.listen(secret.port, function(){
console.log(
@ -195,7 +125,15 @@
'Listening at '+secret.url+
'\n=========================================='
);
checkForUsers();
// Check for clients for each user
User.find({}, function(err, users){
if (err) { console.log(`DB error finding all users: ${err.message}`); }
users.forEach( function(user){
sockets.checkForUsers( io, user.id );
});
});
});
}

View File

@ -9,6 +9,7 @@ body {
width: 100%;
}
/* Alerts */
.centered.alert {
text-align:center;
position: absolute;
@ -17,16 +18,13 @@ body {
transform: translate(-50%, -50%);
}
/* Map and streetview */
#map, #pano {position:relative;}
#pano {float:right;}
.loading {
font-size:7em;
position: absolute;
top: 50%;
left: 50%;
margin: -56px 0 0 -56px;
}
img#panoImg { width:100%; height:100%; }
#notset {display:none}
/* Tracman logo */
.map-logo {
margin-left: -75px;
background: rgba(0,0,0,.7);
@ -35,6 +33,7 @@ body {
}
.map-logo a { color: #fbc93d; }
/* Timestamp */
.tim {
color: #000;
font-size: 12px;
@ -43,6 +42,9 @@ body {
background-color: rgba(255,255,255,.7);
}
/*TODO: Make signs smaller on mobile */
/* Speed sign */
.spd {
font-size: 32px;
height: 40px;}
@ -55,6 +57,7 @@ body {
margin: 10px;
background-color: #FFF;
}
/* Altitude sign */
.alt-unit, .spd-unit { font-size:12px; }
.alt-label, .spd-label {
font-size:18px;
@ -71,3 +74,11 @@ body {
margin: 10px;
background-color: #009800;
}
/* Control buttons */
.btn {
z-index: 50;
background: #222;
} .btn:hover {
background: #333;
}

View File

@ -1,3 +0,0 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}]},{},[1]);

View File

@ -1,3 +1,5 @@
'use strict';
$(document).ready(function(){
// Open drawer with hamburger

View File

@ -1,3 +1,5 @@
'use strict';
jQuery.extend(jQuery.easing,{
easeInOutExpo: function(x, t, b, c, d){
if (t==0) return b;

View File

@ -1,128 +0,0 @@
/*! jQuery UI - v1.11.4 - 2016-06-30
* http://jqueryui.com
* Includes: core.css, tabs.css
* Copyright jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-clearfix {
min-height: 0; /* support: IE7 */
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-tabs {
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
padding: .2em;
}
.ui-tabs .ui-tabs-nav {
margin: 0;
padding: .2em .2em 0;
}
.ui-tabs .ui-tabs-nav li {
list-style: none;
float: left;
position: relative;
top: 0;
margin: 1px .2em 0 0;
border-bottom-width: 0;
padding: 0;
white-space: nowrap;
}
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left;
padding: .5em 1em;
text-decoration: none;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
margin-bottom: -1px;
padding-bottom: 1px;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text;
}
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
cursor: pointer;
}
.ui-tabs .ui-tabs-panel {
display: block;
border-width: 0;
padding: 1em 1.4em;
background: none;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
/*! jQuery UI - v1.11.4 - 2016-06-30
* http://jqueryui.com
* Includes: core.css, tabs.css
* Copyright jQuery Foundation and other contributors; Licensed MIT */
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}

File diff suppressed because one or more lines are too long

View File

@ -1,134 +0,0 @@
/*!
* jQuery UI CSS Framework 1.11.4
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/theming/
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-clearfix {
min-height: 0; /* support: IE7 */
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-tabs {
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
padding: .2em;
}
.ui-tabs .ui-tabs-nav {
margin: 0;
padding: .2em .2em 0;
}
.ui-tabs .ui-tabs-nav li {
list-style: none;
float: left;
position: relative;
top: 0;
margin: 1px .2em 0 0;
border-bottom-width: 0;
padding: 0;
white-space: nowrap;
}
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left;
padding: .5em 1em;
text-decoration: none;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
margin-bottom: -1px;
padding-bottom: 1px;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text;
}
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
cursor: pointer;
}
.ui-tabs .ui-tabs-panel {
display: block;
border-width: 0;
padding: 1em 1.4em;
background: none;
}

View File

@ -1,5 +0,0 @@
/*! jQuery UI - v1.11.4 - 2016-06-30
* http://jqueryui.com
* Copyright jQuery Foundation and other contributors; Licensed MIT */
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}

0
test.js Normal file → Executable file
View File

22
views/help.html Normal file
View File

@ -0,0 +1,22 @@
{% extends 'templates/base.html' %}
{% block title %}{{super()}} | Help{% endblock %}
{% block main %}
<section class='container'>
<h2>Help</h2>
<p><i>Welcome to Tracman! Here's how to get started. </i></p>
<p><u>Set</u> sets your location once using this device's geolocation. On a GPS-enabled phone, the location will be set to its coordinates. </p>
<p><u>Track</u> sets your location as above, but continues to track your location as long as you keep this window open. On a phone, this can drain the battery fast, so be careful! </p>
<p><u>Clear</u> clears your location instantly. Anyone looking at your map will see a blank screen instead. Use this to hide your location. </p>
<p>Share your location by sending the URL to anyone. They won't need an account to view the map. </p>
<a href="/map" class='btn' style="width:60%; position:relative; left:20%; background:#333">Go to map <i class='fa fa-angle-right'></i></a>
</section>
{% endblock %}

View File

@ -5,48 +5,7 @@
{{super()}}
<link href="/static/css/map.css" rel="stylesheet">
<style>
#notset {display:none}
.btn {
z-index: 50;
background: #222;
} .btn:hover {
background: #333;
}
.popup {
padding: 5vw;
border-radius: 5vw;
z-index: 100;
display: none;
position: absolute;
background: #222;
-moz-box-shadow: 3px 4px 6px #000;
-webkit-box-shadow: 3px 4px 6px #000;
box-shadow: 3px 4px 6px #000;
top: calc(60px + 5vw);
bottom: calc(30px + 5vw);
left:5vw; right: 5vw;
overflow-y: auto;
overflow-x: hidden;
}.popup .close {
position: relative;
top: -15px;
left: 10px;
float: right;
}.popup pre {
background: rgba(0,0,0,.5);
border: 1px solid #FFF;
padding: 1vw;
}.popup .btn {
background: #333;
}
/*TODO: Smaller signs on mobile*/
/*.spd-sign {}*/
/*.alt-sign {}*/
.wrap { top:{% if not noHeader %}58{% else %}0{% endif %}px;}
img#panoImg { width:100%; height:100%; }
{% if mapuser.settings.showStreetview and disp!='0' and disp!='1' %}
/* show both */
@ -83,201 +42,6 @@
{% block main %}
{% if user %}
<div class='share popup'>
<style>
.share .fa {
text-align: center;
width: 30px;
height: 30px;
border-radius: 50%;
padding-top: 7px;
background: rgba(255,255,255,.2);
-moz-box-shadow: 2px 2px 4px #000;
-webkit-box-shadow: 2px 2px 4px #000;
box-shadow: 2px 2px 4px #000;
}.share .fa:hover {
background: rgba(255,255,255,.4);
}.share .fa:active {
-moz-box-shadow: 0;
-webkit-box-shadow: 0;
box-shadow: 0;
}
</style>
<div class='close'>
<a href="#" title="close"><i class='fa fa-times'></i></a>
</div>
<h2>Share</h2>
<p>This link will display the map of your location:
<a href="/map/{{user.slug}}">https://tracman.org/map/{{user.slug}}</a>
<br>Here are some buttons for your convienence:
<a href="https://twitter.com/home?status=A%20map%20of%20my%20realtime%20location:%20https://tracman.org/map/{{user.slug}}" target="_blank"><i class="fa fa-twitter"></i></a>
<a href="https://www.facebook.com/sharer/sharer.php?u=https://tracman.org/map/{{user.slug}}" target="_blank"><i class="share fa fa-facebook"></i></a>
<a href="https://www.reddit.com/submit?title=A%20map%20of%20my%20realtime%20location&url=https://tracman.org/map/{{user.slug}}" target="_blank"><i class="fa fa-reddit-alien"></i></a>
<a href="https://www.linkedin.com/shareArticle?mini=true&url=https://tracman.org/map/{{user.slug}}&title=A%20map%20of%20my%20realtime%20location" target="_blank"><i class="share fa fa-linkedin"></i></a>
</p>
<p>You can also embed a map into your website with this code. Be sure to set the width and height attributes to suit your circumstance.
<pre>&lt;iframe src=&quot;https://tracman.org/map/{{user.slug}}?noheader=1&quot; width=&quot;90%&quot; style=&quot;height:90vh&quot;&gt;&lt;/iframe&gt;</pre>
</p>
<a href="#" class='btn' style="width:100%">OK</a>
</div>
<div class='settings popup'>
<div class='close'>
<a href="#" title="close"><i class='fa fa-times'></i></a>
</div>
<h2>Settings</h2>
<script src="/static/js/validator.min.js"></script>
<form id='settings-form' class='col-lg-10 col-lg-offset-1 form-horizontal' data-toggle="validator" role="form" method="post">
<div id='name' class='form-group' title="This appears in your page's title. ">
<label class='control-label col-sm-2 col-lg-3' for="name">Name</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<input class='form-control' name="name" type="text" value="{{user.name}}"
maxlength="160" data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='email' class='form-group' title="For account stuff, no dumb newsletters. ">
<label class='control-label col-sm-2 col-lg-3' for="email">Email</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<input class='form-control' name="email" type="email" value="{{user.email}}"
maxlength="160" data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='slug' class='form-group' title="This is the URL which shows your location. Be careful whom you share it with! ">
<label class='control-label col-sm-2 col-lg-3' for="slug">URL</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<span class='input-group-addon'>tracman.org/map/</span>
<input class='form-control' type="text" name="slug" value="{{user.slug}}" required data-remote="/validate"
maxlength="160" data-remote-error="That URL is already taken. " data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='units' class='form-group col-xs-12' title="Select standard units for feet and miles/hour. Select metric units if you are a commie. ">
<label class='control-label col-sm-4 col-lg-3' for="units">Units</label>
<div class='input-group col-sm-8 col-lg-9'>
<div class='radio-inline'><label>
<input type="radio" name="units" value="standard" {% if user.settings.units == 'standard' %}checked{% endif %}>
Standard
</label></div>
<div class='radio-inline'><label>
<input type="radio" name="units" value="metric" {% if user.settings.units == 'metric' %}checked{% endif %}>
Metric
</label></div>
</div>
</div>
<div id='defaultMap' class='form-group col-xs-12' title="Shows whether to show a satellite image or standard google road map as the default on your page. Visitors will have the option to change this. Note that satellite images load slower. ">
<label class='control-label col-sm-4 col-lg-3' for="map">Default map</label>
<div class='input-group col-sm-8 col-lg-9'>
<div class='radio-inline'><label>
<input type="radio" name="map" value="road" {% if user.settings.defaultMap == 'road' %}checked{% endif %}>
Road
</label></div>
<div class='radio-inline'><label>
<input type="radio" name="map" value="sat" {% if user.settings.defaultMap == 'sat' %}checked{% endif %}>
Satellite
</label></div>
</div>
</div>
<div id='defaultZoom' class='form-group col-xs-12' title="Shows the initial map zoom level on your page. A higher number means more zoom. Note that the size of the viewing window will also have an effect on how much of the map a visitor can see. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="map">Default zoom</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<select class='c-select' name="zoom">
<option {% if user.settings.defaultZoom==1 %}selected {% endif %}value="1">1 World</option>
<option {% if user.settings.defaultZoom==2 %}selected {% endif %}value="2">2</option>
<option {% if user.settings.defaultZoom==3 %}selected {% endif %}value="3">3</option>
<option {% if user.settings.defaultZoom==4 %}selected {% endif %}value="4">4</option>
<option {% if user.settings.defaultZoom==5 %}selected {% endif %}value="5">5 Landmass</option>
<option {% if user.settings.defaultZoom==6 %}selected {% endif %}value="6">6</option>
<option {% if user.settings.defaultZoom==7 %}selected {% endif %}value="7">7</option>
<option {% if user.settings.defaultZoom==8 %}selected {% endif %}value="8">8</option>
<option {% if user.settings.defaultZoom==9 %}selected {% endif %}value="9">9</option>
<option {% if user.settings.defaultZoom==10 %}selected {% endif %}value="10">10 City</option>
<option {% if user.settings.defaultZoom==11 %}selected {% endif %}value="11">11</option>
<option {% if user.settings.defaultZoom==12 %}selected {% endif %}value="12">12</option>
<option {% if user.settings.defaultZoom==13 %}selected {% endif %}value="13">13</option>
<option {% if user.settings.defaultZoom==14 %}selected {% endif %}value="14">14</option>
<option {% if user.settings.defaultZoom==15 %}selected {% endif %}value="15">15 Streets</option>
<option {% if user.settings.defaultZoom==16 %}selected {% endif %}value="16">16</option>
<option {% if user.settings.defaultZoom==17 %}selected {% endif %}value="17">17</option>
<option {% if user.settings.defaultZoom==18 %}selected {% endif %}value="18">18</option>
<option {% if user.settings.defaultZoom==19 %}selected {% endif %}value="19">19</option>
<option {% if user.settings.defaultZoom==20 %}selected {% endif %}value="20">20 Buildings</option>
</select>
</div>
</div>
<div id='showSpeed' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows a spedometer on the map.">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showSpeed">Show speed{% if not user.isPro %} <span class='red'>(PRO)</span>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showSpeed" type="checkbox" {% if not user.isPro %}disabled {% elif user.settings.showSpeed %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='showAltitude' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows the current elevation on the map. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showAlt">Show altitude{% if not user.isPro %} <span class='red'>(PRO)</span>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showAlt" type="checkbox" {% if not user.isPro %}disabled {% elif user.settings.showAlt %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='showStreet' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows a Google street view image at or near your current location, oriented in the direction of travel. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showStreet">Show street view{% if not user.isPro %} <span class='red'>(PRO)</span><br>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showStreet" type="checkbox" {% if not user.isPro %}disabled{% elif user.settings.showStreetview %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='delete' class='form-group col-xs-12'>
<a class='btn red col-xs-5 col-md-3' style='margin-bottom:5px;float:right;' onclick="deleteAccount()">Delete account</a>
</div>
<div id='submit' class='form-group col-xs-12 flexbox' style="padding:0 0 60px">
<input class='btn yellow' style="width:50%;" type="submit" value="Save">
<a href="#" class='btn' style="width:50%">cancel</a>
</div>
</form>
{% if not user.isPro %}<p style="clear:both">Want to try <a href="/pro">Tracman Pro</a>? It's free during beta testing. </p>{% endif %}
<p style="clear:both">Would you like to submit a <a href="https://productpains.com/create/tracman">suggestion</a> or <a href="https://github.com/Tracman-org/Server/issues/new">bug report</a>? </p>
</div>
<div class='help popup'>
<div class='close'>
<a href="#" title="close"><i class='fa fa-times'></i></a>
</div>
<h2>Help</h2>
{% if newuser %}<p><i>Welcome to Tracman! Here's how to get started. </i></p>{% endif %}
<p><u>Set</u> sets your location once using this device's geolocation. On a GPS-enabled phone, the location will be set to its coordinates. </p>
<p><u>Track</u> sets your location as above, but continues to track your location as long as you keep this window open. On a phone, this can drain the battery fast, so be careful! </p>
<p><u>Clear</u> clears your location instantly. Anyone looking at your map will see a blank screen instead. Use this to hide your location. </p>
{% if newuser %}<p>Share your location by sending the URL to anyone. They won't need an account to view the map. </p>{% endif %}
<a href="#" class='btn' style="width:100%">OK</a>
</div>
{% endif %}
<div class='wrap'>
<div id='map'></div>
@ -321,14 +85,13 @@
</div>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="/static/js/bundle.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key={{api}}&callback=gmapsCb" async defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js" integrity="sha256-WKvqiY0jZHWQZIohYEmr9KUC5rEaYEOFTq+ByllJK8w=" crossorigin="anonymous"></script>
<script src="https://maps.googleapis.com/maps/api/js?key={{mapApi}}&callback=gmapsCb" async defer></script>
<script>
/* Variables */ {
var wpid, map, pano, sv, marker, elevator,
sock = io.connect(),
var wpid, map, pano, sv, marker, elevator,
socket = io('//'+window.location.hostname),
mapuserid = {{mapuser._id |dump|safe}},
userid{% if user._id %} = {{user._id |dump|safe}}{% endif %},
settings = JSON.parse('{{mapuser.settings |dump|safe}}'),
@ -339,24 +102,31 @@
panoElem = document.getElementById('pano');
}
// Connect to socket.io
sock.on('connect', function(){
sock.emit('room',
(userid)? 'app-'+userid :mapuserid
);
// }).on('data', function (data) {
// console.log(data);
}).on('error', function (err){
console.error('Unable to connect Socket.IO', err);
});
// Check for location.hash capability (IE9+)
if (!'onhashchange' in window) {
if (!confirm("location.hash won't work in IE<9. If you don't know how to fix this, click cancel. ")){
window.open("https://whatbrowser.org/");
function onConnect(socket,userid,mapuserid){
// Can get location
socket.emit('can-get', mapuserid );
//console.log(`Receiving updates for ${mapuserid}.`);
// Can set location too
if (mapuserid==userid){
socket.emit('can-set', userid );
//console.log(`Sending updates for ${userid}.`);
}
}
// Connect to socket.io
socket.on('connect', function(){
//console.log(`Connected... `);
onConnect(socket,userid,mapuserid);
}).on('reconnect', function(){
//console.log(`Reconnected... `);
onConnect(socket,userid,mapuserid);
}).on('error', function (err){
console.error(`Unable to connect because of: ${err.message}`);
});
// Parse location
function parseLoc(loc) {
loc.spd = (settings.units=='standard')?parseFloat(loc.spd)*2.23694:parseFloat(loc.spd)
@ -474,30 +244,10 @@
updateStreetView(parseLoc(last),10);
}
// Open popups
function setPopups() {
if (location.hash === "#share") {
$('.settings.popup').hide();
$('.share.popup').show();
$('.help.popup').hide();
} else if (location.hash === "#settings") {
$('.settings.popup').show();
$('.share.popup').hide();
$('.help.popup').hide();
} else if (location.hash === "#help") {
$('.settings.popup').hide();
$('.share.popup').hide();
$('.help.popup').show();
} else {
$('.settings.popup').hide();
$('.share.popup').hide();
$('.help.popup').hide();
}
} setPopups(); // Execute immediately
window.onhashchange = setPopups; // Execute on change
// Get location
sock.on('trac', function(loc) {
socket.on('get', function(loc) {
//console.log(`Received location: ${loc.lat}, ${loc.lon}`);
loc = parseLoc(loc);
if (disp!='1' || !settings.showStreetview) {
$('.tim').text('location updated '+loc.time);
@ -531,11 +281,16 @@
lon: pos.coords.longitude,
spd: (pos.coords.speed||0)
}
sock.emit('app',newloc);
socket.broadcast.emit('app', newloc);
toggleMaps(newloc);
}, function(err) {
console.log('done getting position');
if (err) { console.log('ERROR: '+err); }
if (err) {
alert("Unable to set location.");
io.emit('log',err.message);
console.error(err.message); }
else {
//console.log('Set position',pos);
}
}, { enableHighAccuracy:true });
}
}
@ -563,10 +318,13 @@
lon: pos.coords.longitude,
spd: (pos.coords.speed||0)
};
sock.emit('app',newloc);
socket.broadcast.emit('app',newloc);
toggleMaps(newloc);
}, function(err){
alert('Failed to track: \n'+err);
if (err) {
alert("Unable to track location.");
console.log(err.message);
}
}, { enableHighAccuracy:true });
}
}
@ -588,7 +346,7 @@
usr: userid,
lat:0, lon:0, spd:0
}
sock.emit('app',newloc);
socket.broadcast.emit('app',newloc);
toggleMaps(newloc);
}
}

134
views/settings.html Normal file
View File

@ -0,0 +1,134 @@
{% extends 'templates/base.html' %}
{% block title %}{{super()}} | Settings{% endblock %}
{% block main %}
<section class='container'>
<h2>Settings</h2>
<script src="/static/js/validator.min.js"></script>
<form id='settings-form' class='col-lg-10 col-lg-offset-1 form-horizontal' data-toggle="validator" role="form" method="post">
<div id='name' class='form-group' title="This appears in your page's title. ">
<label class='control-label col-sm-2 col-lg-3' for="name">Name</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<input class='form-control' name="name" type="text" value="{{user.name}}"
maxlength="160" data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='email' class='form-group' title="For account stuff, no dumb newsletters. ">
<label class='control-label col-sm-2 col-lg-3' for="email">Email</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<input class='form-control' name="email" type="email" value="{{user.email}}"
maxlength="160" data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='slug' class='form-group' title="This is the URL which shows your location. Be careful whom you share it with! ">
<label class='control-label col-sm-2 col-lg-3' for="slug">URL</label>
<div class='input-group col-xs-12 col-sm-10 col-lg-9'>
<span class='input-group-addon'>tracman.org/map/</span>
<input class='form-control' type="text" name="slug" value="{{user.slug}}" required data-remote="/validate"
maxlength="160" data-remote-error="That URL is already taken. " data-error="Invalid input"><br>
</div>
<div class='help-block with-errors col-xs-12 col-sm-10 col-sm-offset-2 col-lg-9 col-lg-offset-3'></div>
</div>
<div id='units' class='form-group col-xs-12' title="Select standard units for feet and miles/hour. Select metric units if you are a commie. ">
<label class='control-label col-sm-4 col-lg-3' for="units">Units</label>
<div class='input-group col-sm-8 col-lg-9'>
<div class='radio-inline'><label>
<input type="radio" name="units" value="standard" {% if user.settings.units == 'standard' %}checked{% endif %}>
Standard
</label></div>
<div class='radio-inline'><label>
<input type="radio" name="units" value="metric" {% if user.settings.units == 'metric' %}checked{% endif %}>
Metric
</label></div>
</div>
</div>
<div id='defaultMap' class='form-group col-xs-12' title="Shows whether to show a satellite image or standard google road map as the default on your page. Visitors will have the option to change this. Note that satellite images load slower. ">
<label class='control-label col-sm-4 col-lg-3' for="map">Default map</label>
<div class='input-group col-sm-8 col-lg-9'>
<div class='radio-inline'><label>
<input type="radio" name="map" value="road" {% if user.settings.defaultMap == 'road' %}checked{% endif %}>
Road
</label></div>
<div class='radio-inline'><label>
<input type="radio" name="map" value="sat" {% if user.settings.defaultMap == 'sat' %}checked{% endif %}>
Satellite
</label></div>
</div>
</div>
<div id='defaultZoom' class='form-group col-xs-12' title="Shows the initial map zoom level on your page. A higher number means more zoom. Note that the size of the viewing window will also have an effect on how much of the map a visitor can see. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="map">Default zoom</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<select class='c-select' name="zoom">
<option {% if user.settings.defaultZoom==1 %}selected {% endif %}value="1">1 World</option>
<option {% if user.settings.defaultZoom==2 %}selected {% endif %}value="2">2</option>
<option {% if user.settings.defaultZoom==3 %}selected {% endif %}value="3">3</option>
<option {% if user.settings.defaultZoom==4 %}selected {% endif %}value="4">4</option>
<option {% if user.settings.defaultZoom==5 %}selected {% endif %}value="5">5 Landmass</option>
<option {% if user.settings.defaultZoom==6 %}selected {% endif %}value="6">6</option>
<option {% if user.settings.defaultZoom==7 %}selected {% endif %}value="7">7</option>
<option {% if user.settings.defaultZoom==8 %}selected {% endif %}value="8">8</option>
<option {% if user.settings.defaultZoom==9 %}selected {% endif %}value="9">9</option>
<option {% if user.settings.defaultZoom==10 %}selected {% endif %}value="10">10 City</option>
<option {% if user.settings.defaultZoom==11 %}selected {% endif %}value="11">11</option>
<option {% if user.settings.defaultZoom==12 %}selected {% endif %}value="12">12</option>
<option {% if user.settings.defaultZoom==13 %}selected {% endif %}value="13">13</option>
<option {% if user.settings.defaultZoom==14 %}selected {% endif %}value="14">14</option>
<option {% if user.settings.defaultZoom==15 %}selected {% endif %}value="15">15 Streets</option>
<option {% if user.settings.defaultZoom==16 %}selected {% endif %}value="16">16</option>
<option {% if user.settings.defaultZoom==17 %}selected {% endif %}value="17">17</option>
<option {% if user.settings.defaultZoom==18 %}selected {% endif %}value="18">18</option>
<option {% if user.settings.defaultZoom==19 %}selected {% endif %}value="19">19</option>
<option {% if user.settings.defaultZoom==20 %}selected {% endif %}value="20">20 Buildings</option>
</select>
</div>
</div>
<div id='showSpeed' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows a spedometer on the map.">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showSpeed">Show speed{% if not user.isPro %} <span class='red'>(PRO)</span>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showSpeed" type="checkbox" {% if not user.isPro %}disabled {% elif user.settings.showSpeed %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='showAltitude' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows the current elevation on the map. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showAlt">Show altitude{% if not user.isPro %} <span class='red'>(PRO)</span>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showAlt" type="checkbox" {% if not user.isPro %}disabled {% elif user.settings.showAlt %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='showStreet' class='form-group col-xs-12' title="{% if not user.isPro %}PRO ONLY! {% endif %}Shows a Google street view image at or near your current location, oriented in the direction of travel. ">
<label class='control-label col-xs-6 col-sm-4 col-lg-3' for="showStreet">Show street view{% if not user.isPro %} <span class='red'>(PRO)</span><br>{% endif %}</label>
<div class='input-group col-xs-6 col-sm-8 col-lg-9'>
<input class='form-control' name="showStreet" type="checkbox" {% if not user.isPro %}disabled{% elif user.settings.showStreetview %}checked{% else %}{% endif %}><br>
</div>
</div>
<div id='delete' class='form-group col-xs-12'>
<a class='btn red col-xs-5 col-md-3' style='margin-bottom:5px;float:right;' onclick="deleteAccount()">Delete account</a>
</div>
<div id='submit' class='form-group col-xs-12 flexbox' style="padding:0 0 60px">
<input class='btn yellow' style="width:50%; background:#333" type="submit" value="Save">
<a href="#" class='btn' style="width:50%; background:#333">cancel</a>
</div>
</form>
{% if not user.isPro %}<p style="clear:both">Want to try <a href="/pro">Tracman Pro</a>? It's free during beta testing. </p>{% endif %}
<p style="clear:both">Would you like to <a href="https://github.com/Tracman-org/Server/issues/new">submit a suggestion or bug report</a>? </p>
</section>
{% endblock %}

View File

@ -49,6 +49,7 @@
{% block main %}Loading... {% endblock %}
{% if not noFooter %}{% include 'templates/footer.html' %}{% endif %}
<!-- Google Analytics -->
<script>
(function(t,r,a,c,m,o,n){t['GoogleAnalyticsObject']=m;t[m]=t[m]||function(){
(t[m].q=t[m].q||[]).push(arguments);},t[m].l=1*new Date();o=r.createElement(a),
@ -59,6 +60,18 @@
ga('require','linkid');
ga('send','pageview');
</script>
<!-- Firebase -->
<script src="https://www.gstatic.com/firebasejs/3.7.0/firebase.js"></script>
<script>
firebase.initializeApp({
apiKey: "AIzaSyDPYY_Fw3FXLm0hKfIfc8qlrc98zZiN4IY",
authDomain: "tracman-b894f.firebaseapp.com",
databaseURL: "https://tracman-b894f.firebaseio.com",
storageBucket: "tracman-b894f.appspot.com",
messagingSenderId: "483494341936"
});
</script>
</body>
</html>

View File

@ -11,10 +11,9 @@
<ul>
{% if user %}
<li><a href="/map/{{user.slug}}">Map</a></li>
<li><a href="/map#share">Share</a></li>
<li><a href="/map#settings">Settings</a></li>
<li><a href="/settings">Settings</a></li>
{% if user.isAdmin %}<li><a href="/admin">Admin</a></li>{% endif %}
<li><a href="/map#help">Help</a></li>
<li><a href="/help">Help</a></li>
<li><a href="/logout">Logout</a></li>
{% else %}
<li><a href="/#overview">About</a></li>
@ -27,10 +26,10 @@
</header>
<!-- Sitewide notificaton -->
<div class='header alert alert-warning alert-dismissible'>
<strong>Tracman is going through a server migration and various technical issues. Sorry for the inconvienence! <br>You can encourage me to work on it by <a href="https://cash.me/$KeithIrwin">making a donation</a>.
<!--<div class='header alert alert-warning alert-dismissible'>
<strong>Whoops!</strong> Tracman down right now. It will be fixed in a few days.
<a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a>
</div>
</div>-->
{% if error %}
<div class='header alert alert-danger alert-dismissible'>
@ -42,4 +41,4 @@
<strong><i class="fa fa-check-circle"></i> Success!</strong> {{ success | safe }}
<a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a>
</div>
{% endif %}
{% endif %}