Merged release-0.6.7 into master
commit
66017bd7e6
|
@ -1,5 +1,8 @@
|
|||
# Tracman Server Changelog
|
||||
###### v 0.6.6
|
||||
###### v 0.6.7
|
||||
|
||||
#### v0.6.7
|
||||
* [#87](https://github.com/Tracman-org/Server/issues/87) Started loading assets with webpack
|
||||
|
||||
#### v0.6.6
|
||||
|
||||
|
|
15
README.md
15
README.md
|
@ -1,5 +1,5 @@
|
|||
# <img align="left" src="/static/img/icon/by/48.png" alt="[]" title="The Tracman Logo">Tracman
|
||||
###### v 0.6.6
|
||||
###### v 0.6.7
|
||||
|
||||
node.js application to display a sharable map with user's location.
|
||||
|
||||
|
@ -19,11 +19,11 @@ You will need to set up a configuration file at `config/env/env.js`. Use `confi
|
|||
A good method is to simply copy the sample configuration and point `config/env/env.js` to the new version:
|
||||
|
||||
```sh
|
||||
cp config/env/sample.js config/env/my-config.js
|
||||
printf "'use strict';\n\nmodule.exports = require('./my-config.js');" > config/env/env.js
|
||||
cp config/env/sample.js config/env/local-config.js
|
||||
printf "module.exports = require('./local-config.js');" > config/env/env.js
|
||||
```
|
||||
|
||||
Then edit `config/env/my-config.js` to match your local environment.
|
||||
Then edit `config/env/local-config.js` to match your local environment.
|
||||
|
||||
|
||||
## Usage
|
||||
|
@ -52,12 +52,15 @@ Tracman will be updated according to [this branching model](http://nvie.com/post
|
|||
|
||||
[view full changelog](CHANGELOG.md)
|
||||
|
||||
#### v0.6.7
|
||||
* [#87](https://github.com/Tracman-org/Server/issues/87) Started loading assets with webpack
|
||||
|
||||
#### v0.6.6
|
||||
|
||||
* Removed demo recording code
|
||||
* Moved email server settings to env file
|
||||
* Added SMTP check
|
||||
*
|
||||
|
||||
#### v0.6.5
|
||||
|
||||
* [#96](https://github.com/Tracman-org/Server/issues/96) Fixed google streetview
|
||||
|
@ -103,4 +106,4 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/)>.
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
{
|
||||
"verbose": true,
|
||||
"ext": "html, js, json, css",
|
||||
"events": {
|
||||
"start": "npm run minify"
|
||||
}
|
||||
"ext": "html, js, json, css"
|
||||
}
|
15
package.json
15
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tracman",
|
||||
"version": "0.6.6",
|
||||
"version": "0.6.7",
|
||||
"description": "Tracks user's GPS location",
|
||||
"main": "server.js",
|
||||
"dependencies": {
|
||||
|
@ -12,7 +12,9 @@
|
|||
"debug": "^2.6.6",
|
||||
"express": "^4.15.2",
|
||||
"express-validator": "^3.1.3",
|
||||
"jquery": "^3.2.1",
|
||||
"kerberos": "0.0.17",
|
||||
"load-google-maps-api": "^1.0.0",
|
||||
"mellt": "^1.0.0",
|
||||
"minifier": "^0.8.1",
|
||||
"moment": "^2.12.0",
|
||||
|
@ -31,7 +33,8 @@
|
|||
"passport-twitter-token": "^1.3.0",
|
||||
"request": "^2.81.0",
|
||||
"slug": "^0.9.1",
|
||||
"socket.io": "^1.4.4",
|
||||
"socket.io": "^1.7.4",
|
||||
"socket.io-client": "^2.0.3",
|
||||
"xss": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -44,14 +47,16 @@
|
|||
"karma-mocha": "^1.1.1",
|
||||
"mocha": "^2.5.3",
|
||||
"nodemon": "^1.10.2",
|
||||
"supertest": "^1.2.0"
|
||||
"supertest": "^1.2.0",
|
||||
"webpack": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test.js",
|
||||
"start": "node server.js",
|
||||
"nodemon": "nodemon --ignore 'static/**/*.min.*' server.js",
|
||||
"minify": "minify --template .{{filename}}.min.{{ext}} --clean static",
|
||||
"update": "sudo n stable && sudo npm update --save && sudo npm prune"
|
||||
"minify": "minify --template .{{filename}}.min.{{ext}} --clean static/css",
|
||||
"update": "sudo n stable && sudo npm update --save && sudo npm prune",
|
||||
"build": "./node_modules/.bin/webpack --config webpack.config.js"
|
||||
},
|
||||
"repository": "Tracman-org/Server",
|
||||
"keywords": [
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,195 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 52);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 52:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global navigator $ socket userid token mapuser toggleMaps */
|
||||
|
||||
$(function(){
|
||||
|
||||
var wpid, newloc;
|
||||
|
||||
// Set location
|
||||
$('#set-loc').click(function(){
|
||||
if (!userid===mapuser._id){ alert('You are not logged in! '); }
|
||||
else { if (!navigator.geolocation){ alert('Geolocation not enabled. '); }
|
||||
|
||||
else { navigator.geolocation.getCurrentPosition(
|
||||
|
||||
// Success callback
|
||||
function(pos){
|
||||
var newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
spd: (pos.coords.speed||0)
|
||||
};
|
||||
socket.emit('set', newloc);
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Set location:',newloc.lat+", "+newloc.lon);
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(err) {
|
||||
alert("Unable to set location.");
|
||||
console.error('❌️',err.message);
|
||||
},
|
||||
|
||||
// Options
|
||||
{ enableHighAccuracy:true }
|
||||
|
||||
); } }
|
||||
|
||||
});
|
||||
|
||||
// Track location
|
||||
$('#track-loc').click(function(){
|
||||
if (!userid===mapuser._id) { alert('You are not logged in! '); }
|
||||
else {
|
||||
|
||||
// Start tracking
|
||||
if (!wpid) {
|
||||
if (!navigator.geolocation) { alert('Unable to track location. '); }
|
||||
else {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs fa-spin"></i>Stop').prop('title',"Click here to stop tracking your location. ");
|
||||
wpid = navigator.geolocation.watchPosition(
|
||||
|
||||
// Success callback
|
||||
function(pos) {
|
||||
newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
spd: (pos.coords.speed||0)
|
||||
}; socket.emit('set',newloc);
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Set location:',newloc.lat+", "+newloc.lon);
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(err){
|
||||
alert("Unable to track location.");
|
||||
console.error(err.message);
|
||||
},
|
||||
|
||||
// Options
|
||||
{ enableHighAccuracy:true }
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Stop tracking
|
||||
else {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track').prop('title',"Click here to track your location. ");
|
||||
navigator.geolocation.clearWatch(wpid);
|
||||
wpid = undefined;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Clear location
|
||||
$('#clear-loc').click(function(){
|
||||
if (!userid===mapuser._id) { alert('You are not logged in! '); }
|
||||
else {
|
||||
// Stop tracking
|
||||
if (wpid) {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track');
|
||||
navigator.geolocation.clearWatch(wpid);
|
||||
wpid = undefined;
|
||||
}
|
||||
|
||||
// Clear location
|
||||
newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat:0, lon:0, spd:0
|
||||
}; socket.emit('set',newloc);
|
||||
|
||||
// Turn off map
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Cleared location');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -0,0 +1,92 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 22);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 22:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global $ */
|
||||
|
||||
// Push footer to bottom on pages with little content
|
||||
function setFooter(){
|
||||
var windowHeight = $(window).height(),
|
||||
footerBottom = $("footer").offset().top + $("footer").height();
|
||||
if (windowHeight > footerBottom){
|
||||
$("footer").css( "margin-top", windowHeight-footerBottom );
|
||||
}
|
||||
}
|
||||
|
||||
// Execute on page load
|
||||
$(function(){ setFooter(); });
|
||||
|
||||
// Execute on window resize
|
||||
$(window).resize(function(){ setFooter(); });
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -0,0 +1,105 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 21);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 21:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
/* global $ */
|
||||
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
// Open drawer with hamburger
|
||||
$('.hamburger').click(function(){
|
||||
$('.hamburger').toggleClass('is-active');
|
||||
$('nav').toggleClass('visible');
|
||||
});
|
||||
|
||||
// Close drawer after tapping on nav
|
||||
$('nav').click(function(){
|
||||
$('.hamburger').removeClass('is-active');
|
||||
$('nav').removeClass('visible');
|
||||
});
|
||||
|
||||
// Close drawer by tapping outside it
|
||||
$('.wrap, section').click(function(){
|
||||
$('.hamburger').removeClass('is-active');
|
||||
$('nav').removeClass('visible');
|
||||
});
|
||||
|
||||
// Close alerts
|
||||
$('.alert-dismissible .close').click(function() {
|
||||
$(this).parent().slideUp(500);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,195 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 52);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 52:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global navigator $ socket userid token mapuser toggleMaps */
|
||||
|
||||
$(function(){
|
||||
|
||||
var wpid, newloc;
|
||||
|
||||
// Set location
|
||||
$('#set-loc').click(function(){
|
||||
if (!userid===mapuser._id){ alert('You are not logged in! '); }
|
||||
else { if (!navigator.geolocation){ alert('Geolocation not enabled. '); }
|
||||
|
||||
else { navigator.geolocation.getCurrentPosition(
|
||||
|
||||
// Success callback
|
||||
function(pos){
|
||||
var newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
spd: (pos.coords.speed||0)
|
||||
};
|
||||
socket.emit('set', newloc);
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Set location:',newloc.lat+", "+newloc.lon);
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(err) {
|
||||
alert("Unable to set location.");
|
||||
console.error('❌️',err.message);
|
||||
},
|
||||
|
||||
// Options
|
||||
{ enableHighAccuracy:true }
|
||||
|
||||
); } }
|
||||
|
||||
});
|
||||
|
||||
// Track location
|
||||
$('#track-loc').click(function(){
|
||||
if (!userid===mapuser._id) { alert('You are not logged in! '); }
|
||||
else {
|
||||
|
||||
// Start tracking
|
||||
if (!wpid) {
|
||||
if (!navigator.geolocation) { alert('Unable to track location. '); }
|
||||
else {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs fa-spin"></i>Stop').prop('title',"Click here to stop tracking your location. ");
|
||||
wpid = navigator.geolocation.watchPosition(
|
||||
|
||||
// Success callback
|
||||
function(pos) {
|
||||
newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
spd: (pos.coords.speed||0)
|
||||
}; socket.emit('set',newloc);
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Set location:',newloc.lat+", "+newloc.lon);
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(err){
|
||||
alert("Unable to track location.");
|
||||
console.error(err.message);
|
||||
},
|
||||
|
||||
// Options
|
||||
{ enableHighAccuracy:true }
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Stop tracking
|
||||
else {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track').prop('title',"Click here to track your location. ");
|
||||
navigator.geolocation.clearWatch(wpid);
|
||||
wpid = undefined;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Clear location
|
||||
$('#clear-loc').click(function(){
|
||||
if (!userid===mapuser._id) { alert('You are not logged in! '); }
|
||||
else {
|
||||
// Stop tracking
|
||||
if (wpid) {
|
||||
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track');
|
||||
navigator.geolocation.clearWatch(wpid);
|
||||
wpid = undefined;
|
||||
}
|
||||
|
||||
// Clear location
|
||||
newloc = {
|
||||
ts: Date.now(),
|
||||
tok: token,
|
||||
usr: userid,
|
||||
lat:0, lon:0, spd:0
|
||||
}; socket.emit('set',newloc);
|
||||
|
||||
// Turn off map
|
||||
toggleMaps(newloc);
|
||||
console.log('⚜ Cleared location');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -0,0 +1,285 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 55);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 55:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Mellt
|
||||
*
|
||||
* Tests the strength of a password by calculating how long it would take to
|
||||
* brute force it.
|
||||
*
|
||||
* @version 0.1.0
|
||||
* @link http://mel.lt/ The homepage for this script.
|
||||
* @link http://www.hammerofgod.com/passwordcheck.aspx Much of this is based
|
||||
* on the description of Thor's Godly Privacy password strength checker,
|
||||
* however the actual code below is all my own.
|
||||
* @link http://xato.net/passwords/more-top-worst-passwords/ The included
|
||||
* common passwords list is from Mark Burnett's password collection (which
|
||||
* is excellent). You can of course use your own password file instead.
|
||||
*/
|
||||
var Mellt = function() {
|
||||
|
||||
/**
|
||||
* @var integer HashesPerSecond The number of attempts per second you expect
|
||||
* an attacker to be able to attempt. Set to 1 billion by default.
|
||||
*/
|
||||
this.HashesPerSecond = 1000000000;
|
||||
|
||||
/**
|
||||
* @var string CommonPasswords A variable containing an array of common
|
||||
* passwords to check against. If you include common-passwords.js in your
|
||||
* HTML after including Mellt.js, the contents of that file will be used
|
||||
* if this isn't set.
|
||||
* Set this to null (and don't include common-passwords.js) to skip
|
||||
* checking common passwords.
|
||||
*/
|
||||
this.CommonPasswords = null;
|
||||
|
||||
/**
|
||||
* @var array $CharacterSets An array of strings, each string containing a
|
||||
* character set. These should proceed in the order of simplest (0-9) to most
|
||||
* complex (all characters). More complex = more characters.
|
||||
*/
|
||||
this.CharacterSets = [
|
||||
// We're making some guesses here about human nature (again much of this is
|
||||
// based on the TGP password strength checker, and Timothy "Thor" Mullen
|
||||
// deserves the credit for the thinking behind this). Basically we're combining
|
||||
// what we know about users (SHIFT+numbers are more common than other
|
||||
// punctuation for example) combined with how an attacker will attack a
|
||||
// password (most common letters first, expanding outwards).
|
||||
//
|
||||
// If you want to support passwords that use non-english characters, and
|
||||
// your attacker knows this (for example, a Russian site would be expected
|
||||
// to contain passwords in Russian characters) add your characters to one of
|
||||
// the sets below, or create new sets and insert them in the right places.
|
||||
"0123456789",
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789",
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+",
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-=_+[]\"{}|;':,./<>?`~"
|
||||
];
|
||||
};
|
||||
Mellt.prototype = {
|
||||
|
||||
CommonPasswords: null,
|
||||
|
||||
/**
|
||||
* Tests password strength by simulating how long it would take a cracker to
|
||||
* brute force your password.
|
||||
*
|
||||
* Also optionally tests against a list of common passwords (contained in an
|
||||
* external file) to weed out things like "password", which from a pure brute
|
||||
* force perspective would be harder to break if it wasn't so common.
|
||||
*
|
||||
* The character sets being used in this checker assume English (ASCII)
|
||||
* characters (no umlauts for example). If you run a non-english site, and you
|
||||
* suspect the crackers will realize this, you may want to modify the
|
||||
* character set to include the characters in your language.
|
||||
*
|
||||
* @param string $password The password to test the strength of
|
||||
* @return integer Returns an integer specifying how many days it would take
|
||||
* to brute force the password (at 1 billion checks a second) or -1 to
|
||||
* indicate the password was found in the common passwords file. Obviously if
|
||||
* they don't have direct access to the hashed passwords this time would be
|
||||
* longer, and even then most computers (at the time of this writing) won't be
|
||||
* able to test 1 billion hashes a second, but this function measures worst
|
||||
* case scenario, so... I would recommend you require at least 30 days to brute
|
||||
* force a password, obviously more if you're a bank or other secure system.
|
||||
* @throws Exception If an error is encountered.
|
||||
*/
|
||||
CheckPassword: function(password) {
|
||||
|
||||
// First check passwords in the common password file if available.
|
||||
// We do this because "password" takes 129 seconds, but is the first
|
||||
// thing an attacker will try.
|
||||
if (!this.CommonPasswords && Mellt.prototype.CommonPasswords) {
|
||||
this.CommonPasswords = Mellt.prototype.CommonPasswords;
|
||||
}
|
||||
if (this.CommonPasswords) {
|
||||
|
||||
var text = password.toLowerCase();
|
||||
for (var t=0; t<this.CommonPasswords.length; t++) {
|
||||
if (this.CommonPasswords[t]==text) {
|
||||
// If their password exists in the common file, then it's
|
||||
// zero time to crack this terrible password.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out which character set the password is using (based on the most
|
||||
// "complex" character in it).
|
||||
var base = '';
|
||||
var baseKey = null;
|
||||
for (var t=0; t<password.length; t++) {
|
||||
var char = password[t];
|
||||
var foundChar = false;
|
||||
for (var characterSetKey=0; characterSetKey<this.CharacterSets.length; characterSetKey++) {
|
||||
var characterSet = this.CharacterSets[characterSetKey];
|
||||
if (baseKey<=characterSetKey && characterSet.indexOf(char)>-1) {
|
||||
baseKey = characterSetKey;
|
||||
base = characterSet;
|
||||
foundChar = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the character we were looking for wasn't anywhere in any of the
|
||||
// character sets, assign the largest (last) character set as default.
|
||||
if (!foundChar) {
|
||||
base = this.CharacterSets[this.CharacterSets.length-1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Starting at the first character, figure out it's position in the character set
|
||||
// and how many attempts will take to get there. For example, say your password
|
||||
// was an integer (a bank card PIN number for example):
|
||||
// 0 (or 0000 if you prefer) would be the very first password they attempted by the attacker.
|
||||
// 9999 would be the last password they attempted (assuming 4 characters).
|
||||
// Thus a password/PIN of 6529 would take 6529 attempts until the attacker found
|
||||
// the proper combination. The same logic words for alphanumeric passwords, just
|
||||
// with a larger number of possibilities for each position in the password. The
|
||||
// key thing to note is the attacker doesn't need to test the entire range (every
|
||||
// possible combination of all characters) they just need to get to the point in
|
||||
// the list of possibilities that is your password. They can (in this example)
|
||||
// ignore anything between 6530 and 9999. Using this logic, 'aaa' would be a worse
|
||||
// password than 'zzz', because the attacker would encounter 'aaa' first.
|
||||
var attempts = 0;
|
||||
var charactersInBase = base.length;
|
||||
var charactersInPassword = password.length;
|
||||
for (var position=0; position<charactersInPassword; position++) {
|
||||
// We power up to the reverse position in the string. For example, if we're trying
|
||||
// to hack the 4 character PING code in the example above:
|
||||
// First number * (number of characters possible in the charset ^ length of password)
|
||||
// ie: 6 * (10^4) = 6000
|
||||
// then add that same equation for the second number:
|
||||
// 5 * (10^3) = 500
|
||||
// then the third numbers
|
||||
// 2 * (10^2) = 20
|
||||
// and add on the last number
|
||||
// 9
|
||||
// Totals: 6000 + 500 + 20 + 9 = 6529 attempts before we encounter the correct password.
|
||||
var powerOf = charactersInPassword - position - 1;
|
||||
// Character position within the base set. We add one on because strpos is base
|
||||
// 0, we want base 1.
|
||||
var charAtPosition = base.indexOf(password[position])+1;
|
||||
// If we're at the last character, simply add it's position in the character set
|
||||
// this would be the "9" in the pin code example above.
|
||||
if (powerOf==0) {
|
||||
attempts = attempts + charAtPosition;
|
||||
}
|
||||
// Otherwise we need to iterate through all the other characters positions to
|
||||
// get here. For example, to find the 5 in 25 we can't just guess 2 and then 5
|
||||
// (even though Hollywood seems to insist this is possible), we need to try 0,1,
|
||||
// 2,3...15,16,17...23,24,25 (got it).
|
||||
else {
|
||||
// This means we have to try every combination of values up to this point for
|
||||
// all previous characters. Which means we need to iterate through the entire
|
||||
// character set, X times, where X is our position -1. Then we need to multiply
|
||||
// that by this character's position.
|
||||
|
||||
// Multiplier is the (10^4) or (10^3), etc in the pin code example above.
|
||||
var multiplier = Math.pow(charactersInBase,powerOf);
|
||||
// New attempts is the number of attempts we're adding for this position.
|
||||
var newAttempts = charAtPosition * multiplier;
|
||||
// Add that on to our existing number of attempts.
|
||||
attempts = attempts + newAttempts;
|
||||
}
|
||||
}
|
||||
|
||||
// We can (worst case) try a billion passwords a second. Calculate how many days it
|
||||
// will take us to get to the password.
|
||||
var perDay = this.HashesPerSecond*60*60*24;
|
||||
|
||||
// This allows us to calculate a number of days to crack. We use days because anything
|
||||
// that can be cracked in less than a day is basically useless, so there's no point in
|
||||
// having a smaller granularity (hours for example).
|
||||
var days = attempts / perDay;
|
||||
|
||||
// If it's going to take more than a billion days to crack, just return a billion. This
|
||||
// helps when code outside this function isn't using bcmath. Besides, if the password
|
||||
// can survive 2.7 million years it's probably ok.
|
||||
if (days>1000000000) {
|
||||
return 1000000000;
|
||||
}
|
||||
|
||||
return Math.round(days);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -0,0 +1,174 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 54);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 54:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global $ Mellt */
|
||||
|
||||
const mellt = new Mellt();
|
||||
|
||||
function checkMatch(){
|
||||
$('#submit').prop('title',"You need to type your password again before you can save it. ");
|
||||
|
||||
// They match
|
||||
if ( $('#p1').val() === $('#p2').val() ) {
|
||||
$('#submit').prop('disabled',false).prop('title',"Click here to save your password. ");
|
||||
}
|
||||
|
||||
// User has retyped, but they don't match yet
|
||||
else if ($('#p2').val()!=='') {
|
||||
$('#password-help').text("Those passwords don't match... ").css({'color':'#fb6e3d'});
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to type the same password twice before you can save it. ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// On page load
|
||||
$(function(){
|
||||
|
||||
// On typing password
|
||||
$('.password').keyup(function(){
|
||||
|
||||
// Nothing entered
|
||||
if ( $('#p1').val()==='' && $('#p2').val()==='' ){
|
||||
$('#password-help').hide();
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to enter a password first. ");
|
||||
}
|
||||
|
||||
// Only second password entered
|
||||
else if ($('#p1').val()==='') {
|
||||
$('#password-help').show().text("Those passwords don't match... ");
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to type the same password twice correctly before you can save it. ");
|
||||
}
|
||||
|
||||
// At least first password entered
|
||||
else {
|
||||
$('#password-help').show();
|
||||
|
||||
// Check first password
|
||||
var daysToCrack = mellt.CheckPassword($('#p1').val());
|
||||
|
||||
// Not good enough
|
||||
if (daysToCrack<0) {
|
||||
$('#password-help').text("That's is one of the world's most commonly used passwords. You may not use it for Tracman and should not use it anywhere. ").css({'color':'#fb6e3d'});
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to come up with a better password. ");
|
||||
}
|
||||
else if (daysToCrack<1) {
|
||||
$('#password-help').text("That password is pretty bad. It could be cracked in less than a day. Try adding more words, numbers, or symbols. ").css({'color':'#fb6e3d'});
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to come up with a better password. ");
|
||||
}
|
||||
else if (daysToCrack<10) {
|
||||
$('#password-help').text("That password isn't good enough. It could be cracked in "+daysToCrack+" day"+(daysToCrack!=1?'s':'')+". Try adding another word, number, or symbol. ").css({'color':'#fb6e3d'});
|
||||
$('#submit').prop('disabled',true).prop('title',"You need to come up with a better password. ");
|
||||
}
|
||||
|
||||
// Good enough
|
||||
else if (daysToCrack<=30) {
|
||||
$('#password-help').text("That password is good enough, but it could still be cracked in "+daysToCrack+" days. ").css({'color':'#eee'});
|
||||
checkMatch();
|
||||
}
|
||||
else if (daysToCrack<=365) {
|
||||
$('#password-help').text("That password is good. It would take "+daysToCrack+" days to crack. ").css({'color':'#8ae137'});
|
||||
checkMatch();
|
||||
}
|
||||
else if (daysToCrack<1000000000) {
|
||||
var years = Math.round(daysToCrack / 365 * 10) / 10;
|
||||
if (years>1000000) {
|
||||
years = (Math.round(years/1000000*10)/10)+' million';
|
||||
}
|
||||
if (years>1000) {
|
||||
years = (Math.round(years/1000))+' thousand';
|
||||
}
|
||||
$('#password-help').text("That password is great! It could take up to "+years+" years to crack!").css({'color':'#8ae137'});
|
||||
checkMatch();
|
||||
}
|
||||
else {
|
||||
$('#password-help').text("That password is amazing! It is virtually impossible to crack!").css({'color':'#8ae137'});
|
||||
checkMatch();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// On checking 'show'
|
||||
$('#show').click(function(){
|
||||
if ($(this).is(':checked')) {
|
||||
$('.password').attr('type','text');
|
||||
} else {
|
||||
$('.password').attr('type','password');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
|
@ -0,0 +1,231 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 53);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 53:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global location $ */
|
||||
|
||||
// Validate email addresses
|
||||
function validateEmail(email) {
|
||||
var re = /^(([^<>()\[\]\\.,;:\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,}))$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
// Replace inputed value with response
|
||||
function replaceFromEndpoint(type, selector, cb) {
|
||||
$.get('/validate?'+type+'='+$(selector).val())
|
||||
.done(function(data){
|
||||
$(selector).val(data);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
// On page load
|
||||
$(function(){
|
||||
var slugNotUnique, emailNotUnique;
|
||||
|
||||
// Set timezone in password change link
|
||||
$('#password').attr('href',"/settings/password?tz="+new Date().getTimezoneOffset());
|
||||
|
||||
// Delete account
|
||||
$('#delete').click(function(){
|
||||
if (confirm("Are you sure you want to delete your account? This CANNOT be undone! ")) {
|
||||
window.location.href = "/settings/delete";
|
||||
}
|
||||
});
|
||||
|
||||
function validateForm(input) {
|
||||
|
||||
// Perform basic check, then validate uniqueness
|
||||
basicCheck(function(){ validateUniqueness(input); });
|
||||
|
||||
function basicCheck(cb){
|
||||
var checkedCount = 0;
|
||||
|
||||
// Check slug
|
||||
if (!$('#slug-input').val()){
|
||||
$('#slug-help').show().text("A slug is required. ");
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a slug. ");
|
||||
if (checkedCount>0) {cb();} else {checkedCount++;}
|
||||
}
|
||||
else {
|
||||
if (!slugNotUnique){ $('#slug-help').hide(); }
|
||||
if (checkedCount>0) {cb();} else {checkedCount++;}
|
||||
}
|
||||
|
||||
// Check email
|
||||
if (!$('#email-input').val()){
|
||||
$('#email-help').show().text("An email is required. ");
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to enter an email address. ");
|
||||
if (checkedCount>0) {cb();} else {checkedCount++;}
|
||||
}
|
||||
else if (!validateEmail($('#email-input').val())) {
|
||||
$('#email-help').show().text("You must enter a valid email address. ");
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a valid email address. ");
|
||||
if (checkedCount>0) {cb();} else {checkedCount++;}
|
||||
}
|
||||
else {
|
||||
if (!emailNotUnique){ $('#email-help').hide(); }
|
||||
if (checkedCount>0) {cb();} else {checkedCount++;}
|
||||
}
|
||||
}
|
||||
|
||||
function validateUniqueness(input){
|
||||
|
||||
function recheckBasic(){
|
||||
if ($('#email-help').is(":visible") && $('#email-help').text().substring(0,25)!=="Unable to confirm unique ") {
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different email address. ");
|
||||
}
|
||||
else if ($('#slug-help').is(":visible") && $('#slug-help').text().substring(0,25)!=="Unable to confirm unique ") {
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different slug. ");
|
||||
}
|
||||
else if ( $('#slug-help').text().substring(0,25)==="Unable to confirm unique " ) {
|
||||
$('#submit-group .main').prop('title',"Unable to confirm unique slug with the server. This might not work... ");
|
||||
}
|
||||
else if ( $('#email-help').text().substring(0,25)==="Unable to confirm unique " ) {
|
||||
$('#submit-group .main').prop('title',"Unable to confirm unique email with the server. This might not work... ");
|
||||
}
|
||||
else {
|
||||
$('#submit-group .main').prop('disabled',false).prop('title',"Click here to save your changes. ");
|
||||
}
|
||||
}
|
||||
|
||||
// Should server be queried for unique values?
|
||||
if (input && $('#'+input+'-input').val()) {
|
||||
if (input==='email' && !validateEmail($('#email-input').val())) {}
|
||||
|
||||
// Query server for unique values
|
||||
else {
|
||||
$.ajax({
|
||||
url: '/validate?'+input+'='+$('#'+input+'-input').val(),
|
||||
type: 'GET',
|
||||
statusCode: {
|
||||
|
||||
// Is unique
|
||||
200: function(){
|
||||
$('#'+input+'-help').hide();
|
||||
if (input==='slug'){ slugNotUnique=false; }
|
||||
else if (input==='email'){ emailNotUnique=false; }
|
||||
recheckBasic();
|
||||
},
|
||||
|
||||
// Isn't unique
|
||||
400: function(){
|
||||
if (input==='slug'){ slugNotUnique=true; }
|
||||
else if (input==='email'){ emailNotUnique=true; }
|
||||
$('#'+input+'-help').show().text("That "+input+" is already in use by another user. ");
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to supply a different "+input+". ");
|
||||
}
|
||||
|
||||
} })
|
||||
|
||||
// Server error
|
||||
.error( function(){
|
||||
if (input==='slug'){ slugNotUnique=undefined; }
|
||||
else if (input==='email'){ emailNotUnique=undefined; }
|
||||
$('#'+input+'-help').show().text("Unable to confirm unique "+input+". This might not work... ");
|
||||
recheckBasic();
|
||||
});
|
||||
|
||||
} }
|
||||
|
||||
// Nothing changed. Recheck basic validations
|
||||
else { recheckBasic(); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Input change listeners
|
||||
$('#slug-input').change(function(){
|
||||
if (!$('#slug-input').val()){
|
||||
$('#slug-help').show().text("A slug is required. ");
|
||||
$('#submit-group .main').prop('disabled',true).prop('title',"You need to enter a slug. ");
|
||||
}
|
||||
else {
|
||||
$('#slug-help').hide();
|
||||
replaceFromEndpoint('slugify','#slug-input',function(){
|
||||
validateForm('slug');
|
||||
});
|
||||
}
|
||||
});
|
||||
$('#email-input').change(function(){
|
||||
validateForm('email');
|
||||
});
|
||||
$('#name-input').change(function(){
|
||||
replaceFromEndpoint('xss','#name-input',validateForm);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
316
static/js/map.js
316
static/js/map.js
|
@ -1,316 +0,0 @@
|
|||
'use strict';
|
||||
/* global $ io google mapuser userid disp noHeader */
|
||||
|
||||
|
||||
// Variables
|
||||
var map, pano, marker, elevator, newLoc;
|
||||
const mapElem = document.getElementById('map'),
|
||||
panoElem = document.getElementById('pano'),
|
||||
socket = io('//'+window.location.hostname);
|
||||
|
||||
function waitForElements(vars,cb){
|
||||
if ( vars.every(function(v){ return v!==undefined; }) ){
|
||||
cb();
|
||||
} else {
|
||||
setTimeout(waitForElements(vars,cb), 100);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// socket.io stuff
|
||||
socket
|
||||
.on('connect', function(){
|
||||
console.log("⬆️ Connected!");
|
||||
waitForElements([mapuser,userid], function() {
|
||||
onConnect(socket,userid,mapuser._id);
|
||||
});
|
||||
})
|
||||
.on('disconnect', function(){
|
||||
console.log("⬇️ Disconnected!");
|
||||
})
|
||||
.on('error', function (err){
|
||||
console.error('❌️',err.message);
|
||||
});
|
||||
|
||||
// Parse location
|
||||
function parseLoc(loc) {
|
||||
loc.spd = (mapuser.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);
|
||||
loc.tim = new Date(loc.tim).toLocaleString();
|
||||
loc.glatlng = new google.maps.LatLng(loc.lat, loc.lon);
|
||||
return loc;
|
||||
}
|
||||
|
||||
// Show/hide map if location is set/unset
|
||||
function toggleMaps(loc) {
|
||||
if (loc.lat===0&&loc.lon===0) {
|
||||
$('#map').hide();
|
||||
$('#pano').hide();
|
||||
$('#notset').show();
|
||||
}
|
||||
else {
|
||||
$('#map').show();
|
||||
$('#pano').show();
|
||||
$('#notset').hide();
|
||||
}
|
||||
}
|
||||
// Toggle maps on page load
|
||||
$(function() {
|
||||
toggleMaps(mapuser.last);
|
||||
});
|
||||
|
||||
// Google maps API callback
|
||||
window.gmapsCb = function() {
|
||||
//console.log("gmapsCb() called");
|
||||
|
||||
// Make sure everything's ready...
|
||||
waitForElements([mapuser,disp,noHeader], function() {
|
||||
|
||||
// Create map
|
||||
if (disp!=='1') {
|
||||
//console.log("Creating map...");
|
||||
|
||||
map = new google.maps.Map( mapElem, {
|
||||
center: new google.maps.LatLng( mapuser.last.lat, mapuser.last.lon ),
|
||||
panControl: false,
|
||||
scaleControl: mapuser.settings.showScale,
|
||||
draggable: false,
|
||||
zoom: mapuser.settings.defaultZoom,
|
||||
streetViewControl: false,
|
||||
zoomControlOptions: {position: google.maps.ControlPosition.LEFT_TOP},
|
||||
mapTypeId: (mapuser.settings.defaultMap=='road')?google.maps.MapTypeId.ROADMAP:google.maps.MapTypeId.HYBRID
|
||||
});
|
||||
marker = new google.maps.Marker({
|
||||
position: { lat:mapuser.last.lat, lng:mapuser.last.lon },
|
||||
title: mapuser.name,
|
||||
map: map,
|
||||
draggable: false
|
||||
});
|
||||
map.addListener('zoom_changed',function(){
|
||||
map.setCenter(marker.getPosition());
|
||||
});
|
||||
|
||||
// Create iFrame logo
|
||||
if (noHeader!=='0') {
|
||||
//console.log("Creating iFrame logo...");
|
||||
const logoDiv = document.createElement('div');
|
||||
logoDiv.id = 'map-logo';
|
||||
logoDiv.innerHTML = '<a href="https://tracman.org/">'+
|
||||
'<img src="https://tracman.org/static/img/style/logo-28.png" alt="[]">'+
|
||||
"<span class='text'>Tracman</span></a>";
|
||||
map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(logoDiv);
|
||||
}
|
||||
|
||||
// Create update time block
|
||||
//console.log("Creating time block...");
|
||||
const timeDiv = document.createElement('div');
|
||||
timeDiv.id = 'timestamp';
|
||||
if (mapuser.last.time) {
|
||||
timeDiv.innerHTML = 'location updated '+new Date(mapuser.last.time).toLocaleString();
|
||||
}
|
||||
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(timeDiv);
|
||||
|
||||
// Create speed block
|
||||
if (mapuser.settings.showSpeed) {
|
||||
//console.log("Creating speed sign...");
|
||||
const speedSign = document.createElement('div'),
|
||||
speedLabel = document.createElement('div'),
|
||||
speedText = document.createElement('div'),
|
||||
speedUnit = document.createElement('div');
|
||||
speedLabel.id = 'spd-label';
|
||||
speedLabel.innerHTML = 'SPEED';
|
||||
speedText.id = 'spd';
|
||||
speedText.innerHTML = (mapuser.settings.units=='standard')?(parseFloat(mapuser.last.spd)*2.23694).toFixed():mapuser.last.spd.toFixed();
|
||||
speedUnit.id = 'spd-unit';
|
||||
speedUnit.innerHTML = (mapuser.settings.units=='standard')?'m.p.h.':'k.p.h.';
|
||||
speedSign.id = 'spd-sign';
|
||||
speedSign.appendChild(speedLabel);
|
||||
speedSign.appendChild(speedText);
|
||||
speedSign.appendChild(speedUnit);
|
||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(speedSign);
|
||||
}
|
||||
|
||||
// Create altitude block
|
||||
if (mapuser.settings.showAlt) {
|
||||
//console.log("Creating altitude sign...");
|
||||
const elevator = new google.maps.ElevationService,
|
||||
altitudeSign = document.createElement('div'),
|
||||
altitudeLabel = document.createElement('div'),
|
||||
altitudeText = document.createElement('div'),
|
||||
altitudeUnit = document.createElement('div');
|
||||
altitudeLabel.id = 'alt-label';
|
||||
altitudeText.id = 'alt';
|
||||
altitudeUnit.id = 'alt-unit';
|
||||
altitudeSign.id = 'alt-sign';
|
||||
altitudeText.innerHTML = '';
|
||||
altitudeLabel.innerHTML = 'ALTITUDE';
|
||||
getAltitude(new google.maps.LatLng(mapuser.last.lat,mapuser.last.lon), elevator, function(alt) {
|
||||
if (alt) { altitudeText.innerHTML = (mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed(); }
|
||||
});
|
||||
altitudeUnit.innerHTML = (mapuser.settings.units=='standard')?'feet':'meters';
|
||||
altitudeSign.appendChild(altitudeLabel);
|
||||
altitudeSign.appendChild(altitudeText);
|
||||
altitudeSign.appendChild(altitudeUnit);
|
||||
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(altitudeSign);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create streetview
|
||||
if (disp!=='0' && mapuser.settings.showStreetview) {
|
||||
//console.log("Creating streetview...");
|
||||
updateStreetView(parseLoc(mapuser.last),10);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Got location
|
||||
socket.on('get', function(loc) {
|
||||
console.log("🌐️ Got location:",loc.lat+", "+loc.lon);
|
||||
|
||||
// Parse location
|
||||
newLoc = parseLoc(loc);
|
||||
|
||||
// Update map
|
||||
if (disp!=='1') {
|
||||
|
||||
// Update time
|
||||
$('#timestamp').text('location updated '+newLoc.tim);
|
||||
|
||||
// Update marker and map center
|
||||
google.maps.event.trigger(map,'resize');
|
||||
map.setCenter({ lat:newLoc.lat, lng:newLoc.lon });
|
||||
marker.setPosition({ lat:newLoc.lat, lng:newLoc.lon });
|
||||
|
||||
// Update speed
|
||||
if (mapuser.settings.showSpeed) {
|
||||
$('#spd').text( newLoc.spd.toFixed() );
|
||||
}
|
||||
|
||||
// Update altitude
|
||||
if (mapuser.settings.showAlt) {
|
||||
getAltitude({
|
||||
lat: newLoc.lat,
|
||||
lng: newLoc.lon
|
||||
}, elevator, function(alt) {
|
||||
if (alt) {
|
||||
$('#alt').text( (mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed() );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update street view
|
||||
if (disp!=='0' && mapuser.settings.showStreetview) {
|
||||
updateStreetView(newLoc,10);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Check altitude
|
||||
function getAltitude(loc,elev,cb){
|
||||
//console.log("Getting altitude...");
|
||||
elev = elev || new google.maps.ElevationService;
|
||||
elev.getElevationForLocations({
|
||||
'locations': [loc]
|
||||
}, function(results, status) {
|
||||
if (status === google.maps.ElevationStatus.OK && results[0]) {
|
||||
cb(results[0].elevation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get street view imagery
|
||||
function getStreetViewData(loc,rad,cb) {
|
||||
// Ensure that the location hasn't changed
|
||||
if (loc===newLoc) {
|
||||
if (!sv) { var sv=new google.maps.StreetViewService(); }
|
||||
sv.getPanorama({
|
||||
location: {
|
||||
lat: loc.lat,
|
||||
lng: loc.lon
|
||||
},
|
||||
radius: rad
|
||||
}, function(data,status){ switch (status){
|
||||
// Success
|
||||
case google.maps.StreetViewStatus.OK:
|
||||
cb(data);
|
||||
break;
|
||||
// No results in that radius
|
||||
case google.maps.StreetViewStatus.ZERO_RESULTS:
|
||||
// Try again with a bigger radius
|
||||
getStreetViewData(loc,rad*2,cb);
|
||||
break;
|
||||
// Error
|
||||
default:
|
||||
console.error(new Error('❌️ Street view not available: '+status).message);
|
||||
} });
|
||||
} else { console.log('loc!==newLoc'); }
|
||||
}
|
||||
|
||||
// Update streetview
|
||||
function updateStreetView(loc) {
|
||||
//console.log("Updating streetview...");
|
||||
|
||||
// Moving (show stationary image)
|
||||
if (loc.spd>1) {
|
||||
const imgElem = document.getElementById('panoImg');
|
||||
getStreetViewData(loc, 2, function(data){
|
||||
if (!imgElem) {
|
||||
// Create image
|
||||
pano = undefined;
|
||||
$('#pano').empty();
|
||||
$('#pano').append($('<img>',{
|
||||
alt: 'Street view image',
|
||||
src: 'https://maps.googleapis.com/maps/api/streetview?size=800x800&location='+loc.lat+','+loc.lon+'&fov=90&heading='+loc.dir+'&key={{api}}',
|
||||
id: 'panoImg'
|
||||
}));
|
||||
}
|
||||
// Set image
|
||||
$('#panoImg').attr('src','https://maps.googleapis.com/maps/api/streetview?size='+$('#pano').width()+'x'+$('#pano').height()+'&location='+data.location.latLng.lat()+','+data.location.latLng.lng()+'&fov=90&heading='+loc.dir+'&key={{api}}');
|
||||
});
|
||||
}
|
||||
|
||||
// Not moving and pano not set (create panoramic image)
|
||||
else if (pano==null) {
|
||||
getStreetViewData(loc, 2, function(data){
|
||||
// Create panorama
|
||||
$('#pano').empty();
|
||||
const panoOptions = {
|
||||
panControl: false,
|
||||
zoomControl: false,
|
||||
addressControl: false,
|
||||
linksControl: false,
|
||||
motionTracking: false,
|
||||
motionTrackingControl: false
|
||||
};
|
||||
pano = new google.maps.StreetViewPanorama(panoElem, panoOptions);
|
||||
// Set panorama
|
||||
pano.setPano(data.location.pano);
|
||||
pano.setPov({
|
||||
pitch: 0,
|
||||
// Point towards users's location from street
|
||||
heading: Math.atan((loc.lon-data.location.latLng.lng())/(loc.lat-data.location.latLng.lat()))*(180/Math.PI)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
/* global navigator $ socket userid token mapuser toggleMaps */
|
||||
|
||||
$(function(){
|
||||
|
||||
var wpid, newloc;
|
||||
|
||||
// Set location
|
|
@ -0,0 +1,304 @@
|
|||
'use strict';
|
||||
/* global mapuser userid disp noHeader mapKey */
|
||||
|
||||
// Webpack imports
|
||||
import io from 'socket.io-client';
|
||||
import $ from 'jquery';
|
||||
import loadGoogleMapsAPI from 'load-google-maps-api';
|
||||
|
||||
// Variables
|
||||
var map, pano, marker, elevator, newLoc;
|
||||
const mapElem = document.getElementById('map'),
|
||||
panoElem = document.getElementById('pano'),
|
||||
socket = io('//'+window.location.hostname);
|
||||
|
||||
// socket.io stuff
|
||||
socket
|
||||
.on('connect', function(){
|
||||
console.log("⬆️ Connected!");
|
||||
|
||||
// Can get location
|
||||
socket.emit('can-get', mapuser._id );
|
||||
console.log("🚹 Receiving updates for",mapuser._id);
|
||||
|
||||
// Can set location too
|
||||
if (mapuser._id===userid) {
|
||||
socket.emit('can-set', userid );
|
||||
console.log("🚹 Sending updates for",userid);
|
||||
}
|
||||
|
||||
})
|
||||
.on('disconnect', function(){
|
||||
console.log("⬇️ Disconnected!");
|
||||
})
|
||||
.on('error', function (err){
|
||||
console.error('❌️',err.message);
|
||||
});
|
||||
|
||||
// Show/hide map if location is set/unset
|
||||
function toggleMaps(loc) {
|
||||
if (loc.lat===0&&loc.lon===0) {
|
||||
$('#map').hide();
|
||||
$('#pano').hide();
|
||||
$('#notset').show();
|
||||
}
|
||||
else {
|
||||
$('#map').show();
|
||||
$('#pano').show();
|
||||
$('#notset').hide();
|
||||
}
|
||||
}
|
||||
// Toggle maps on page load
|
||||
$(function() {
|
||||
toggleMaps(mapuser.last);
|
||||
});
|
||||
|
||||
// Load google maps
|
||||
loadGoogleMapsAPI({ key:mapKey })
|
||||
.then(function(googlemaps) {
|
||||
//console.log("Loaded google maps:",googlemaps);
|
||||
|
||||
// Create map
|
||||
if (disp!=='1') {
|
||||
//console.log("Creating map...");
|
||||
|
||||
map = new googlemaps.Map( mapElem, {
|
||||
center: new googlemaps.LatLng( mapuser.last.lat, mapuser.last.lon ),
|
||||
panControl: false,
|
||||
scaleControl: mapuser.settings.showScale,
|
||||
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
|
||||
});
|
||||
marker = new googlemaps.Marker({
|
||||
position: { lat:mapuser.last.lat, lng:mapuser.last.lon },
|
||||
title: mapuser.name,
|
||||
map: map,
|
||||
draggable: false
|
||||
});
|
||||
map.addListener('zoom_changed',function(){
|
||||
map.setCenter(marker.getPosition());
|
||||
});
|
||||
|
||||
// Create iFrame logo
|
||||
if (noHeader!=='0') {
|
||||
//console.log("Creating iFrame logo...");
|
||||
const logoDiv = document.createElement('div');
|
||||
logoDiv.id = 'map-logo';
|
||||
logoDiv.innerHTML = '<a href="https://tracman.org/">'+
|
||||
'<img src="https://tracman.org/static/img/style/logo-28.png" alt="[]">'+
|
||||
"<span class='text'>Tracman</span></a>";
|
||||
map.controls[googlemaps.ControlPosition.BOTTOM_LEFT].push(logoDiv);
|
||||
}
|
||||
|
||||
// Create update time block
|
||||
//console.log("Creating time block...");
|
||||
const timeDiv = document.createElement('div');
|
||||
timeDiv.id = 'timestamp';
|
||||
if (mapuser.last.time) {
|
||||
timeDiv.innerHTML = 'location updated '+new Date(mapuser.last.time).toLocaleString();
|
||||
}
|
||||
map.controls[googlemaps.ControlPosition.RIGHT_BOTTOM].push(timeDiv);
|
||||
|
||||
// Create speed block
|
||||
if (mapuser.settings.showSpeed) {
|
||||
//console.log("Creating speed sign...");
|
||||
const speedSign = document.createElement('div'),
|
||||
speedLabel = document.createElement('div'),
|
||||
speedText = document.createElement('div'),
|
||||
speedUnit = document.createElement('div');
|
||||
speedLabel.id = 'spd-label';
|
||||
speedLabel.innerHTML = 'SPEED';
|
||||
speedText.id = 'spd';
|
||||
speedText.innerHTML = (mapuser.settings.units=='standard')?(parseFloat(mapuser.last.spd)*2.23694).toFixed():mapuser.last.spd.toFixed();
|
||||
speedUnit.id = 'spd-unit';
|
||||
speedUnit.innerHTML = (mapuser.settings.units=='standard')?'m.p.h.':'k.p.h.';
|
||||
speedSign.id = 'spd-sign';
|
||||
speedSign.appendChild(speedLabel);
|
||||
speedSign.appendChild(speedText);
|
||||
speedSign.appendChild(speedUnit);
|
||||
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(speedSign);
|
||||
}
|
||||
|
||||
// Create altitude block
|
||||
if (mapuser.settings.showAlt) {
|
||||
//console.log("Creating altitude sign...");
|
||||
const elevator = new googlemaps.ElevationService,
|
||||
altitudeSign = document.createElement('div'),
|
||||
altitudeLabel = document.createElement('div'),
|
||||
altitudeText = document.createElement('div'),
|
||||
altitudeUnit = document.createElement('div');
|
||||
altitudeLabel.id = 'alt-label';
|
||||
altitudeText.id = 'alt';
|
||||
altitudeUnit.id = 'alt-unit';
|
||||
altitudeSign.id = 'alt-sign';
|
||||
altitudeText.innerHTML = '';
|
||||
altitudeLabel.innerHTML = 'ALTITUDE';
|
||||
getAltitude(new googlemaps.LatLng(mapuser.last.lat,mapuser.last.lon), elevator, function(alt) {
|
||||
if (alt) { altitudeText.innerHTML = (mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed(); }
|
||||
});
|
||||
altitudeUnit.innerHTML = (mapuser.settings.units=='standard')?'feet':'meters';
|
||||
altitudeSign.appendChild(altitudeLabel);
|
||||
altitudeSign.appendChild(altitudeText);
|
||||
altitudeSign.appendChild(altitudeUnit);
|
||||
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(altitudeSign);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create streetview
|
||||
if (disp!=='0' && mapuser.settings.showStreetview) {
|
||||
//console.log("Creating streetview...");
|
||||
updateStreetView(parseLoc(mapuser.last),10);
|
||||
}
|
||||
|
||||
// Parse location
|
||||
function parseLoc(loc) {
|
||||
loc.spd = (mapuser.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);
|
||||
loc.tim = new Date(loc.tim).toLocaleString();
|
||||
loc.glatlng = new googlemaps.LatLng(loc.lat, loc.lon);
|
||||
return loc;
|
||||
}
|
||||
|
||||
// Got location
|
||||
socket.on('get', function(loc) {
|
||||
console.log("🌐️ Got location:",loc.lat+", "+loc.lon);
|
||||
|
||||
// Parse location
|
||||
newLoc = parseLoc(loc);
|
||||
|
||||
// Update map
|
||||
if (disp!=='1') {
|
||||
|
||||
// Update time
|
||||
$('#timestamp').text('location updated '+newLoc.tim);
|
||||
|
||||
// Update marker and map center
|
||||
googlemaps.event.trigger(map,'resize');
|
||||
map.setCenter({ lat:newLoc.lat, lng:newLoc.lon });
|
||||
marker.setPosition({ lat:newLoc.lat, lng:newLoc.lon });
|
||||
|
||||
// Update speed
|
||||
if (mapuser.settings.showSpeed) {
|
||||
$('#spd').text( newLoc.spd.toFixed() );
|
||||
}
|
||||
|
||||
// Update altitude
|
||||
if (mapuser.settings.showAlt) {
|
||||
getAltitude({
|
||||
lat: newLoc.lat,
|
||||
lng: newLoc.lon
|
||||
}, elevator, function(alt) {
|
||||
if (alt) {
|
||||
$('#alt').text( (mapuser.settings.units=='standard')?(alt*3.28084).toFixed():alt.toFixed() );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update street view
|
||||
if (disp!=='0' && mapuser.settings.showStreetview) {
|
||||
updateStreetView(newLoc,10);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Check altitude
|
||||
function getAltitude(loc,elev,cb){
|
||||
//console.log("Getting altitude...");
|
||||
elev = elev || new googlemaps.ElevationService;
|
||||
elev.getElevationForLocations({
|
||||
'locations': [loc]
|
||||
}, function(results, status) {
|
||||
if (status === googlemaps.ElevationStatus.OK && results[0]) {
|
||||
cb(results[0].elevation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get street view imagery
|
||||
function getStreetViewData(loc,rad,cb) {
|
||||
// Ensure that the location hasn't changed
|
||||
if (loc===newLoc) {
|
||||
if (!sv) { var sv=new googlemaps.StreetViewService(); }
|
||||
sv.getPanorama({
|
||||
location: {
|
||||
lat: loc.lat,
|
||||
lng: loc.lon
|
||||
},
|
||||
radius: rad
|
||||
}, function(data,status){ switch (status){
|
||||
// Success
|
||||
case googlemaps.StreetViewStatus.OK:
|
||||
cb(data);
|
||||
break;
|
||||
// No results in that radius
|
||||
case googlemaps.StreetViewStatus.ZERO_RESULTS:
|
||||
// Try again with a bigger radius
|
||||
getStreetViewData(loc,rad*2,cb);
|
||||
break;
|
||||
// Error
|
||||
default:
|
||||
console.error(new Error('❌️ Street view not available: '+status).message);
|
||||
} });
|
||||
} else { console.log('loc!==newLoc'); }
|
||||
}
|
||||
|
||||
// Update streetview
|
||||
function updateStreetView(loc) {
|
||||
//console.log("Updating streetview...");
|
||||
|
||||
// Moving (show stationary image)
|
||||
if (loc.spd>1) {
|
||||
const imgElem = document.getElementById('panoImg');
|
||||
getStreetViewData(loc, 2, function(data){
|
||||
if (!imgElem) {
|
||||
// Create image
|
||||
pano = undefined;
|
||||
$('#pano').empty();
|
||||
$('#pano').append($('<img>',{
|
||||
alt: 'Street view image',
|
||||
src: 'https://maps.googleapis.com/maps/api/streetview?size=800x800&location='+loc.lat+','+loc.lon+'&fov=90&heading='+loc.dir+'&key={{api}}',
|
||||
id: 'panoImg'
|
||||
}));
|
||||
}
|
||||
// Set image
|
||||
$('#panoImg').attr('src','https://maps.googleapis.com/maps/api/streetview?size='+$('#pano').width()+'x'+$('#pano').height()+'&location='+data.location.latLng.lat()+','+data.location.latLng.lng()+'&fov=90&heading='+loc.dir+'&key={{api}}');
|
||||
});
|
||||
}
|
||||
|
||||
// Not moving and pano not set (create panoramic image)
|
||||
else if (pano==null) {
|
||||
getStreetViewData(loc, 2, function(data){
|
||||
// Create panorama
|
||||
$('#pano').empty();
|
||||
const panoOptions = {
|
||||
panControl: false,
|
||||
zoomControl: false,
|
||||
addressControl: false,
|
||||
linksControl: false,
|
||||
motionTracking: false,
|
||||
motionTrackingControl: false
|
||||
};
|
||||
pano = new googlemaps.StreetViewPanorama(panoElem, panoOptions);
|
||||
// Set panorama
|
||||
pano.setPano(data.location.pano);
|
||||
pano.setPov({
|
||||
pitch: 0,
|
||||
// Point towards users's location from street
|
||||
heading: Math.atan((loc.lon-data.location.latLng.lng())/(loc.lat-data.location.latLng.lat()))*(180/Math.PI)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
|
@ -94,19 +94,21 @@
|
|||
|
||||
{% block javascript %}
|
||||
{{super()}}
|
||||
|
||||
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js" integrity="sha256-WKvqiY0jZHWQZIohYEmr9KUC5rEaYEOFTq+ByllJK8w=" crossorigin="anonymous"></script>
|
||||
<script type="application/javascript" src="https://maps.googleapis.com/maps/api/js?key={{mapApi}}&callback=gmapsCb" async defer></script>
|
||||
<script type="application/javascript" src="/static/js/.map.min.js"></script>
|
||||
{% if user.id == mapuser.id %}<script type="application/javascript" src="/static/js/.map-controls.min.js"></script>{% endif %}
|
||||
<script> /* Variables from server */
|
||||
|
||||
|
||||
<!-- Variables from server-side -->
|
||||
<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}}";
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Webpacked bundles -->
|
||||
<script type="application/javascript" src="/static/js/dist/map.js"></script>
|
||||
{% if user.id == mapuser.id %}
|
||||
<script type="application/javascript" src="/static/js/dist/controls.js"></script>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -53,7 +53,7 @@
|
|||
|
||||
{% block javascript %}
|
||||
{{super()}}
|
||||
<script type="application/javascript" src="/static/js/.mellt.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/.common-passwords.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/.password.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/mellt.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/commonpasswords.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/password.js"></script>
|
||||
{% endblock %}
|
|
@ -150,5 +150,5 @@
|
|||
|
||||
{% block javascript %}
|
||||
{{super()}}
|
||||
<script type="application/javascript" src="/static/js/.settings.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/settings.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<a href="https://cash.me/$KeithIrwin"><i class="fa fa-dollar"></i></a>
|
||||
<a href="bitcoin:14VN8GzWQPssWQherCE5XNGBWzy3eCDn74?label=tracman"><i class="fa fa-btc"></i></a>
|
||||
<br>
|
||||
<a href="/privacy">Privacy Policy</a> ▪️ <a href="/terms">Terms of Service</a>
|
||||
<a href="/privacy">Privacy Policy</a> |️ <a href="/terms">Terms of Service</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="application/javascript" src="/static/js/.footer.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/footer.js"></script>
|
|
@ -56,4 +56,4 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<script type="application/javascript" src="/static/js/.header.min.js"></script>
|
||||
<script type="application/javascript" src="/static/js/dist/header.js"></script>
|
|
@ -0,0 +1,18 @@
|
|||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
header: './static/js/src/header.js',
|
||||
footer: './static/js/src/footer.js',
|
||||
map: './static/js/src/map.js',
|
||||
controls: './static/js/src/map-controls.js',
|
||||
settings: './static/js/src/settings.js',
|
||||
password: './static/js/src/password.js',
|
||||
mellt: './static/js/src/mellt.js',
|
||||
commonpasswords: './static/js/src/common-passwords.js'
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'static/js/dist')
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue