Commit
parent
797beecb8f
commit
3a00dc8372
11
README.md
11
README.md
|
@ -1,5 +1,5 @@
|
|||
# Tracman
|
||||
###### v 0.3.0
|
||||
###### v 0.4.0
|
||||
|
||||
node.js application to display a map with user's location.
|
||||
|
||||
|
@ -16,7 +16,14 @@ $ npm start
|
|||
|
||||
## Changelog
|
||||
|
||||
#### v0.4.0
|
||||
|
||||
* Opened registration
|
||||
* Replaced 'Imperial' with 'Standard'
|
||||
* Bug fixes
|
||||
|
||||
#### v0.3.0
|
||||
|
||||
* Unified map and dashboard UI
|
||||
* Security updates
|
||||
* Security updates
|
||||
* New admin UI
|
107
config/auth.js
107
config/auth.js
|
@ -11,60 +11,65 @@ passport.use(new GoogleStrategy({
|
|||
callbackURL: secret.url+'/auth/google/callback',
|
||||
passReqToCallback: true
|
||||
}, function(req, accessToken, refreshToken, profile, done) {
|
||||
// Check for user
|
||||
User.findOne({googleID: profile.id}, function(err, user){
|
||||
|
||||
// error
|
||||
if (err) { console.log('Error finding user with google ID: '+profile.id+'\n'+err); }
|
||||
|
||||
// User found
|
||||
if (!err && user !== null) {
|
||||
if (!user.name) {
|
||||
user.name = profile.displayName;
|
||||
}
|
||||
user.lastLogin = Date.now();
|
||||
user.save(function (err, raw) {
|
||||
if (err) { throwErr(req,err); }
|
||||
});
|
||||
done(null, user);
|
||||
}
|
||||
|
||||
// User not found
|
||||
else {
|
||||
|
||||
if (req.session.passport) /* create new user */ {
|
||||
User.findById(req.session.passport.user, function(err,user){
|
||||
if (err) {
|
||||
console.log('Error finding invited user with passport session ID: '+req.session.passport.user+'\n'+err);
|
||||
var failMessage = 'Something went wrong finding your session. Would you like to <a href="/bug">report this error</a>?'; }
|
||||
else {
|
||||
user.googleID = profile.id;
|
||||
user.lastLogin = Date.now();
|
||||
crypto.randomBytes(32, function(err,buf) {
|
||||
if (err) {console.log('Unable to get random bytes:',err);}
|
||||
if (!buf) {console.log('Unable to get random buffer');}
|
||||
else {
|
||||
user.sk32 = buf.toString('hex');
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
console.log('Error saving new (invited) user '+err);
|
||||
var failMessage = 'Something went wrong finding your session. Would you like to <a href="/bug">report this error</a>?';
|
||||
} else { successMessage = 'Your account has been created. Next maybe you should download the <a href="/android">android app</a>. ' }
|
||||
done(null, user, { success:successMessage, failure:failMessage });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check for user
|
||||
User.findOne({googleID: profile.id}, function(err, user){
|
||||
|
||||
// Error
|
||||
if (err) { console.log('Error finding user with google ID: '+profile.id+'\n'+err); }
|
||||
|
||||
// User found
|
||||
if (!err && user !== null) /* Log user in */ {
|
||||
if (!user.name) { user.name=profile.displayName; }
|
||||
user.lastLogin = Date.now();
|
||||
user.save(function (err, raw) {
|
||||
if (err) { throwErr(req,err); }
|
||||
}); done(null, user);
|
||||
}
|
||||
|
||||
// User not found
|
||||
else /* create user */ {
|
||||
var user, successMessage, failMessage, cbc=2;
|
||||
user.googleID = profile.id;
|
||||
user.email = profile.emails[0];
|
||||
user.lastLogin = Date.now();
|
||||
// Get slug
|
||||
(function checkSlug(s,cb) {
|
||||
//console.log('checking ',s);
|
||||
User.findOne({slug:s}, function(err, existingUser){
|
||||
if (err) { console.log('Slug check error for ',slug(request.name).toLowerCase(),+':',err); }
|
||||
if (existingUser){
|
||||
s = '';
|
||||
while (s.length<6) {
|
||||
s+='abcdefghijkmnpqrtuvwxy346789'.charAt(Math.floor(Math.random()*28));
|
||||
}
|
||||
checkSlug(s,cb);
|
||||
} else { cb(s); }
|
||||
});
|
||||
})(slug(profile.name).toLowerCase(), function(newSlug){
|
||||
user.slug = newSlug;
|
||||
if (cbc>1) /* waiting on other calls */ { cbc--; }
|
||||
else { done(null, user, { success:successMessage, failure:failMessage }); }
|
||||
});
|
||||
// Get sk32
|
||||
crypto.randomBytes(32, function(err,buf) {
|
||||
if (err) {console.log('Unable to get random bytes:',err);}
|
||||
if (!buf) {console.log('Unable to get random buffer');}
|
||||
else {
|
||||
user.sk32 = buf.toString('hex');
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
console.log('Error saving new (invited) user '+err);
|
||||
var failMessage = 'Something went wrong finding your session. Would you like to <a href="/bug">report this error</a>?';
|
||||
} else { successMessage = 'Your account has been created. Next maybe you should download the <a href="/android">android app</a>. ' }
|
||||
if (cbc>1) /* waiting on other calls */ { cbc--; }
|
||||
else { done(null, user, { success:successMessage, failure:failMessage }); }
|
||||
});
|
||||
}
|
||||
|
||||
else /* user wasn't invited */ {
|
||||
done(null,false, {error: 'User not found. Maybe you want to <a href="#" data-scrollto="get">request an invite</a>? '});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
|
|
|
@ -14,16 +14,14 @@ var throwErr = function(req,err){
|
|||
|
||||
var ensureAuth = function(req,res,next){
|
||||
if (req.isAuthenticated()) { return next(); }
|
||||
else {
|
||||
req.flash('last',req.path);
|
||||
res.redirect('/login');
|
||||
}
|
||||
else { res.redirect('/login'); }
|
||||
};
|
||||
|
||||
var ensureAdmin = function(req,res,next){
|
||||
ensureAuth(req,res,function(){
|
||||
if (req.user.isAdmin){ return next(); }
|
||||
else { next(); }
|
||||
//TODO: test this by logging in as !isAdmin and go to /admin
|
||||
// else if (!res.headersSent) { // 404 to users (not admin)
|
||||
// var err = new Error('404: Not found: '+req.url);
|
||||
// err.status = 404;
|
||||
|
|
|
@ -11,7 +11,7 @@ module.exports = mongoose.model('User', {
|
|||
lastLogin: Date,
|
||||
googleID: {type:Number, unique:true},
|
||||
settings: {
|
||||
units: {type:String, default:'imperial'},
|
||||
units: {type:String, default:'standard'},
|
||||
defaultMap: {type:String, default:'road'},
|
||||
defaultZoom: {type:Number, default:11},
|
||||
showSpeed: {type:Boolean, default:false},
|
||||
|
|
|
@ -40,7 +40,6 @@ router.route('/')
|
|||
|
||||
});
|
||||
|
||||
|
||||
router.route('/requests')
|
||||
.all(mw.ensureAdmin, function(req,res,next){
|
||||
if (err) {
|
||||
|
|
|
@ -26,8 +26,10 @@ router.route('/')
|
|||
|
||||
});
|
||||
// Not logged in
|
||||
} else {
|
||||
// Show index
|
||||
}
|
||||
|
||||
// Not logged in
|
||||
else {
|
||||
res.render('index.html', {
|
||||
error: req.flash('error')[0],
|
||||
success: req.flash('success')[0],
|
||||
|
@ -35,34 +37,7 @@ router.route('/')
|
|||
inviteError: req.flash('request-error')[0]
|
||||
});
|
||||
}
|
||||
}).post(function(req,res){ // Create request
|
||||
Request.findOne({email:req.body.email}, function(err, request) {
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
if (request){ // Already requested with this email
|
||||
req.flash('request-error', 'Invite already requested! ');
|
||||
res.redirect('/#get');
|
||||
} else { // Send new request
|
||||
request = new Request({
|
||||
name: req.body.name,
|
||||
email: req.body.email,
|
||||
beg: req.body.why,
|
||||
requestedTime: Date.now()
|
||||
}); request.save(function(err) {
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
mail.mailgun.messages().send({
|
||||
from: 'Tracman Requests <requests@tracman.org>',
|
||||
to: 'Keith Irwin <tracman@keithirwin.us>',
|
||||
subject: 'New Tracman Invite request',
|
||||
html: '<p>'+req.body.name+' requested a Tracman invite. </p><p>'+req.body.why+'</p><p><a href="https://tracman.org/admin/requests">See all invites</a></p>',
|
||||
text: '\n'+req.body.name+' requested a Tracman invite. \n\n'+req.body.why+'\n\nhttps://tracman.org/admin/requests'
|
||||
}, function(err,body){
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
else { req.flash('request-success', 'Invite requested! '); }
|
||||
res.redirect('/#get');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -1,72 +0,0 @@
|
|||
var router = require('express').Router(),
|
||||
mw = require('../middleware.js'),
|
||||
slug = require('slug'),
|
||||
User = require('../models/user.js'),
|
||||
Request = require('../models/request.js');
|
||||
|
||||
router.get('/:invite', function(req,res,next){
|
||||
function associateUser(request,user){
|
||||
request.userId = user._id;
|
||||
request.save(function(err, raw){
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
});
|
||||
req.logIn(user, function(err) {
|
||||
if (err) { mw.throwErr(req,err); }
|
||||
user.lastLogin = Date.now();
|
||||
user.save(function(err, raw) {
|
||||
if (err) { mw.throwErr(req,err); }
|
||||
res.redirect('/login');
|
||||
});
|
||||
});
|
||||
}
|
||||
User.findOne({requestId:req.params.invite}, function(err, existingUser) { // User already accepted invite
|
||||
if (err) { console.log('Could not find existing user: '+err); }
|
||||
if (existingUser && existingUser.gooogleID) { res.redirect('/login'); }
|
||||
else {
|
||||
Request.findById(req.params.invite, function(err, request) { // Check for granted invite
|
||||
if (err) { mw.throwErr(req,err); }
|
||||
if (!request) { next(); }
|
||||
else {
|
||||
if (existingUser) { // associate existing user with google account
|
||||
associateUser(request,existingUser);
|
||||
} else { // create new user
|
||||
(function checkSlug(s,cb) {
|
||||
console.log('checking ',s);
|
||||
User.findOne({slug:s}, function(err, existingUser){
|
||||
if (err) { console.log('Slug check error for ',slug(request.name).toLowerCase(),+':',err); }
|
||||
if (existingUser){
|
||||
s = '';
|
||||
while (s.length<6) {
|
||||
s+='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.charAt(Math.floor(Math.random()*62));
|
||||
}
|
||||
checkSlug(s,cb);
|
||||
} else { cb(s); }
|
||||
});
|
||||
})(slug(request.name).toLowerCase(), function(newSlug){
|
||||
newUser = new User({ // Create new user
|
||||
requestId: request._id,
|
||||
email: '',
|
||||
slug: newSlug,
|
||||
name: request.name,
|
||||
created: Date.now(),
|
||||
settings: {
|
||||
units: 'imperial',
|
||||
showSpeed: false,
|
||||
showTemp: false,
|
||||
showAlt: false,
|
||||
showStreetview: true
|
||||
}
|
||||
})
|
||||
newUser.save(function(err) {
|
||||
if (err) { mw.throwErr(req,err); }
|
||||
associateUser(request,newUser);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tracman",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"description": "Tracks user's GPS location",
|
||||
"main": "server.js",
|
||||
"dependencies": {
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
require('./config/routes/misc.js')
|
||||
);
|
||||
app.use(['/map','/trac'], require('./config/routes/map.js'));
|
||||
app.use('/invited', require('./config/routes/invite.js'));
|
||||
app.use('/admin', require('./config/routes/admin.js'));
|
||||
app.use('/static', express.static(__dirname+'/static'));
|
||||
}
|
||||
|
|
|
@ -178,30 +178,30 @@
|
|||
color:#fb6e3d;
|
||||
}
|
||||
|
||||
.get {
|
||||
.join {
|
||||
background: #fbc93d;
|
||||
}
|
||||
.get input, .get textarea {
|
||||
.join input, .join textarea {
|
||||
color:#111;
|
||||
}
|
||||
.get .input {
|
||||
.join .input {
|
||||
width: 47%;
|
||||
float: left;
|
||||
}
|
||||
.get .submit {
|
||||
.join .submit {
|
||||
width: 47%;
|
||||
float:right;
|
||||
}
|
||||
.get .input:nth-of-type(odd) {
|
||||
.join .input:nth-of-type(odd) {
|
||||
margin-right: 6%;
|
||||
}
|
||||
.get .message {
|
||||
.join .message {
|
||||
display: block;
|
||||
clear: both;
|
||||
float: none;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.get .input input {
|
||||
.join .input input {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
@ -209,7 +209,7 @@
|
|||
border: 0;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.get .message textarea {
|
||||
.join .message textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
|
@ -218,19 +218,19 @@
|
|||
padding: 10px 15px;
|
||||
resize: vertical;
|
||||
}
|
||||
.get label {
|
||||
.join label {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
.get label.input span, .get label.message span {
|
||||
.join label.input span, .join label.message span {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
.get .submit {
|
||||
.join .submit {
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.get .submit .btn, .get .submit .alert {
|
||||
.join .submit .btn, .join .submit .alert {
|
||||
position:static;
|
||||
float:right;
|
||||
}
|
||||
|
@ -289,18 +289,18 @@
|
|||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
.get .input {
|
||||
.join .input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
.get .input:nth-of-type(odd) {
|
||||
.join .input:nth-of-type(odd) {
|
||||
margin-right: 0;
|
||||
}
|
||||
.get label {
|
||||
.join label {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.get label:first-of-type {
|
||||
.join label:first-of-type {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends 'templates/base.html' %}
|
||||
{% block title %}Tracman | Invite Requests{% endblock %}
|
||||
{% block title %}{{super()}} | Admin{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
|
@ -13,6 +13,7 @@
|
|||
<section class='dark'>
|
||||
<div class='container' id='tabs'>
|
||||
<ul class='nav nav-tabs'>
|
||||
<!--TODO: Why is bootstrap not formatting .nav-tabs -->
|
||||
<li><a href="#users">Users</a></li>
|
||||
<li><a href="#requests">Requests</a></li>
|
||||
</ul>
|
||||
|
@ -36,7 +37,7 @@
|
|||
<td id='{{usr.id}}-created'></td>
|
||||
<td id='{{usr.id}}-logged'></td>
|
||||
<td id='{{usr.id}}-moved'></td>
|
||||
<td id='{{usr.id}}-edit'><form action="" method="POST">
|
||||
<td id='{{usr.id}}-edit'><form action="/users" method="POST">
|
||||
<button type="submit" class='btn btn-block btn-danger' name="delete" value="{{usr.id}}">DELETE</button>
|
||||
</form></td>
|
||||
</tr>
|
||||
|
@ -65,7 +66,7 @@
|
|||
<td>{{ request.beg | replace("\r\n", "<br>") | safe }}</td>
|
||||
<td id='{{request.id}}-requested'></td>
|
||||
<td id='{{request.id}}-edit'>
|
||||
<form action="" method="POST">
|
||||
<form action="/requests" method="POST">
|
||||
{% if not request.granted %}
|
||||
<button type="submit" class='btn btn-block btn-success' name="invite" value="{{request.id}}">INVITE</button>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
{% extends 'templates/base.html' %}
|
||||
{% block title %}Tracman | Invite Requests{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.container { max-width:90%; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<section class='dark'>
|
||||
<div class='container'>
|
||||
<h1>Requests</h1>
|
||||
|
||||
<table id='requests-table' class='table table-hover'>
|
||||
<thead><tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Message</th>
|
||||
|
||||
<th>Requested</th>
|
||||
<th>Invited</th>
|
||||
<th>User</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for request in requests %}
|
||||
<tr class="table-{% if request.userId %}success{% elif request.granted %}info{% else %}danger{% endif %}">
|
||||
<td>{{request.name}}</td>
|
||||
<td>{{request.email}}</td>
|
||||
<td>{{ request.beg | replace("\r\n", "<br>") | safe }}</td>
|
||||
<td id='{{request.id}}-requested'></td>
|
||||
<td id='{{request.id}}-edit'>
|
||||
<form action="" method="POST">
|
||||
{% if not request.granted %}
|
||||
<button type="submit" class='btn btn-block btn-success' name="invite" value="{{request.id}}">INVITE</button>
|
||||
{% endif %}
|
||||
<button type="submit" class='btn btn-block btn-danger' name="delete" value="{{request.id}}">DELETE</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
{% if request.userId %}
|
||||
<a href="/map/id/{{request.userId}}">{{request.userId}}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/static/js/moment.min.js"></script>
|
||||
<script>
|
||||
{% for request in requests %}
|
||||
$('#{{request.id}}-requested').text(
|
||||
moment("{{request.requestedTime}}", "ddd MMM DD YYYY HH:mm:ss [GMT]ZZ").fromNow()
|
||||
// Sun Mar 20 2016 19:21:55 GMT+0100 (CET)
|
||||
);
|
||||
{% if request.granted %}
|
||||
$('#{{request.id}}-edit').text(
|
||||
moment("{{request.granted}}", "ddd MMM DD YYYY HH:mm:ss [GMT]ZZ").fromNow()
|
||||
);
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -1,69 +0,0 @@
|
|||
{% extends 'templates/base.html' %}
|
||||
{% block title %}Tracman | Users{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.container { max-width:90%; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<section class='dark'>
|
||||
<div class='container'>
|
||||
<h1>Users</h1>
|
||||
|
||||
<table id='users-table' class='table table-hover'>
|
||||
<thead><tr>
|
||||
<th>Name</th>
|
||||
<th>Slug</th>
|
||||
<th>Joined</th>
|
||||
<th>Last login</th>
|
||||
<th>Moved</th>
|
||||
<th>Edit</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for usr in users %}
|
||||
<tr>
|
||||
<td>{{usr.name}}</td>
|
||||
<td><a href="/map/{{usr.slug}}">/{{usr.slug}}</a></td>
|
||||
<td id='{{usr.id}}-created'></td>
|
||||
<td id='{{usr.id}}-logged'></td>
|
||||
<td id='{{usr.id}}-moved'></td>
|
||||
<td id='{{usr.id}}-edit'><form action="" method="POST">
|
||||
<button type="submit" class='btn btn-block btn-danger' name="delete" value="{{usr.id}}">DELETE</button>
|
||||
</form></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/static/js/moment.min.js"></script>
|
||||
<script>
|
||||
{% for usr in users %}
|
||||
{% if usr.created %}
|
||||
$('#{{usr.id}}-created').text(
|
||||
moment("{{usr.created}}", "ddd MMM DD YYYY HH:mm:ss [GMT]ZZ").format('l')
|
||||
);
|
||||
{% endif %}
|
||||
|
||||
{% if usr.lastLogin %}
|
||||
$('#{{usr.id}}-logged').text(
|
||||
moment("{{usr.lastLogin}}", "ddd MMM DD YYYY HH:mm:ss [GMT]ZZ").fromNow()
|
||||
);
|
||||
{% endif %}
|
||||
|
||||
{% if usr.last.time %}
|
||||
$('#{{usr.id}}-moved').text(
|
||||
moment("{{usr.last.time}}", "ddd MMM DD YYYY HH:mm:ss [GMT]ZZ").fromNow()
|
||||
);
|
||||
{% else %}
|
||||
$('#{{usr.id}}-moved').text("never");
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -23,12 +23,12 @@
|
|||
<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 imperial units for feet and miles/hour. Select metric units if you are a commie. ">
|
||||
<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="imperial" {% if user.settings.units == 'imperial' %}checked{% endif %}>
|
||||
Imperial
|
||||
<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 %}>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{% endif %}
|
||||
{% else %}
|
||||
<a class='btn' href="/map/keith">View example<i class='fa fa-angle-right'></i></a>
|
||||
<a class='btn' href="#" data-scrollto="get">Request invite<i class='fa fa-angle-down'></i></a>
|
||||
<a class='btn' href="#" data-scrollto="join">Join<i class='fa fa-angle-down'></i></a>
|
||||
<a class='btn' href="/login">Login<i class='fa fa-angle-right'></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -110,24 +110,12 @@
|
|||
</section>
|
||||
|
||||
{% if not user %}
|
||||
<section class='get light' id='get'>
|
||||
<section class='join light' id='join'>
|
||||
<div class='container'>
|
||||
<h2>Hook me up!</h2>
|
||||
<h3>Right now, Tracman is invite-only. You can beg me for access here. </h3>
|
||||
<form id='invite-form' method="post">
|
||||
<label class='input'><span>Name</span><input type="text" name="name" required></label>
|
||||
<label class='input'><span>Email address</span><input type="email" name="email" required></label>
|
||||
<label class='message'><span>Why you deserve beta access</span><textarea id='why' name="why"></textarea></label>
|
||||
<label class='submit'>
|
||||
{% if inviteSuccess.length > 0 %}
|
||||
<div class='alert alert-success'><i class="fa fa-check-circle"></i> {{ inviteSuccess }}</div>
|
||||
{% elif inviteError.length > 0 %}
|
||||
<div class='alert alert-danger'><i class="fa fa-exclamation-circle"></i> {{ inviteError }}</div>
|
||||
{% else %}
|
||||
<button type="submit" class='btn'>Request Invite<i class='fa fa-angle-right'></i></button>
|
||||
{% endif %}
|
||||
</label>
|
||||
</form>
|
||||
<h3>Right now, Tracman is in beta testing. Things may break. </h3>
|
||||
<p>You will need a google account to join tracman and log in. </p>
|
||||
<a class='btn btn-lg ' href="/login">Join Tracman</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
@ -145,12 +145,12 @@
|
|||
<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 imperial units for feet and miles/hour. Select metric units if you are a commie. ">
|
||||
<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="imperial" {% if user.settings.units == 'imperial' %}checked{% endif %}>
|
||||
Imperial
|
||||
<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 %}>
|
||||
|
@ -316,7 +316,7 @@
|
|||
|
||||
// Parse location
|
||||
function parseLoc(loc) {
|
||||
loc.spd = (settings.units=='imperial')?parseFloat(loc.spd)*2.23694:parseFloat(loc.spd)
|
||||
loc.spd = (settings.units=='standard')?parseFloat(loc.spd)*2.23694:parseFloat(loc.spd)
|
||||
loc.dir = parseFloat(loc.dir);
|
||||
loc.lat = parseFloat(loc.lat);
|
||||
loc.lon = parseFloat(loc.lon);
|
||||
|
@ -392,9 +392,9 @@
|
|||
speedLabel.className = 'spd-label';
|
||||
speedLabel.innerHTML = 'SPEED';
|
||||
speedText.className = 'spd';
|
||||
speedText.innerHTML = (settings.units=='imperial')?(parseFloat(last.spd)*2.23694).toFixed():last.spd.toFixed();
|
||||
speedText.innerHTML = (settings.units=='standard')?(parseFloat(last.spd)*2.23694).toFixed():last.spd.toFixed();
|
||||
speedUnit.className = 'spd-unit';
|
||||
speedUnit.innerHTML = (settings.units=='imperial')?'m.p.h.':'k.p.h.';
|
||||
speedUnit.innerHTML = (settings.units=='standard')?'m.p.h.':'k.p.h.';
|
||||
speedSign.className = 'spd-sign';
|
||||
speedSign.appendChild(speedLabel);
|
||||
speedSign.appendChild(speedText);
|
||||
|
@ -416,9 +416,9 @@
|
|||
altitudeText.innerHTML = '';
|
||||
altitudeLabel.innerHTML = 'ALTITUDE';
|
||||
getAltitude(new google.maps.LatLng(last.lat,last.lon), elevator, function(alt) {
|
||||
if (alt) { altitudeText.innerHTML = (settings.units=='imperial')?(alt*3.28084).toFixed():alt.toFixed(); }
|
||||
if (alt) { altitudeText.innerHTML = (settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed(); }
|
||||
});
|
||||
altitudeUnit.innerHTML = (settings.units=='imperial')?'feet above sea level':'meters above sea level';
|
||||
altitudeUnit.innerHTML = (settings.units=='standard')?'feet above sea level':'meters above sea level';
|
||||
altitudeSign.appendChild(altitudeLabel);
|
||||
altitudeSign.appendChild(altitudeText);
|
||||
altitudeSign.appendChild(altitudeUnit);
|
||||
|
@ -453,7 +453,7 @@
|
|||
if (settings.showSpeed) { $('.spd').text(loc.spd.toFixed()); }
|
||||
if (settings.showAlt) {
|
||||
getAltitude({lat:loc.lat,lng:loc.lon}, elevator, function(alt) {
|
||||
if (alt) { $('.alt').text((settings.units=='imperial')?(alt*3.28084).toFixed():alt.toFixed()); }
|
||||
if (alt) { $('.alt').text((settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed()); }
|
||||
});
|
||||
}
|
||||
toggleMaps(loc);
|
||||
|
|
Loading…
Reference in New Issue