reorganize, document, clean javascript code

This commit is contained in:
Bartek Fabiszewski 2019-04-13 19:45:51 +02:00
parent 7acc3df67d
commit 7f1170187c
11 changed files with 2328 additions and 1495 deletions

View File

@ -28,6 +28,7 @@ body {
a { a {
color: #bce; color: #bce;
text-decoration: none; text-decoration: none;
cursor: pointer;
} }
:link, :visited { :link, :visited {
color: #bce; color: #bce;
@ -105,9 +106,19 @@ select {
border-radius: 11px 0 0 11px; border-radius: 11px 0 0 11px;
cursor: pointer; cursor: pointer;
} }
label[for=user] {
#user, #track, #summary, #export, #import, #other, #units { padding-top: 1em;
display: block;
}
.section {
padding-bottom: 10px; padding-bottom: 10px;
display: block;
}
.section:first-child {
padding-top: 1em;
}
#inputFile {
display: none;
} }
#summary div { #summary div {
padding-top: .3em; padding-top: .3em;
@ -190,16 +201,14 @@ select {
background-color: white; background-color: white;
opacity: 0.8; opacity: 0.8;
} }
#close { #chart_close {
position: fixed; position: fixed;
bottom: 175px; bottom: 175px;
right: 175px; right: 175px;
z-index: 10001; z-index: 10001;
font-size: 0.8em; font-size: 0.8em;
}
#close a, #close:link, #close:visited {
color: #5070af; color: #5070af;
cursor: pointer;
} }
.mi { .mi {

View File

@ -27,7 +27,9 @@
*/ */
class uAuth { class uAuth {
/** @var bool Is user authenticated */
private $isAuthenticated = false; private $isAuthenticated = false;
/** @var uUser|null User */
public $user = null; public $user = null;
public function __construct() { public function __construct() {
@ -107,7 +109,7 @@
/** /**
* Process log in request * Process log in request
* *
* @return void * @return boolean
*/ */
public function checkLogin($login, $pass) { public function checkLogin($login, $pass) {
if (!is_null($login) && !is_null($pass)) { if (!is_null($login) && !is_null($pass)) {

View File

@ -27,59 +27,108 @@
* Handles config values * Handles config values
*/ */
class uConfig { class uConfig {
// version number /**
* @var string Version number
*/
static $version = "1.0-beta"; static $version = "1.0-beta";
// default map drawing framework /**
* @var string Default map drawing framework
*/
static $mapapi = "openlayers"; static $mapapi = "openlayers";
// gmaps key /**
* @var string|null Google maps key
*/
static $gkey = null; static $gkey = null;
// openlayers additional map layers /**
* @var array Openlayers additional map layers
*/
static $ol_layers = []; static $ol_layers = [];
// default coordinates for initial map /**
* @var float Default latitude for initial map
*/
static $init_latitude = 52.23; static $init_latitude = 52.23;
/**
* @var float Default longitude for initial map
*/
static $init_longitude = 21.01; static $init_longitude = 21.01;
// MySQL config /**
static $dbdsn = ""; // database dsn * @var string Database dsn
static $dbuser = ""; // database user */
static $dbpass = ""; // database pass static $dbdsn = "";
static $dbprefix = ""; // optional table names prefix, eg. "ulogger_" /**
* @var string Database user
*/
static $dbuser = "";
/**
* @var string Database pass
*/
static $dbpass = "";
/**
* @var string Optional table names prefix, eg. "ulogger_"
*/
static $dbprefix = "";
// require login/password authentication /**
* @var bool Require login/password authentication
*/
static $require_authentication = true; static $require_authentication = true;
// all users tracks are visible to authenticated user /**
* @var bool All users tracks are visible to authenticated user
*/
static $public_tracks = false; static $public_tracks = false;
// admin user who has access to all users locations /**
// none if empty * @var string Admin user who has access to all users locations
* none if empty
*/
static $admin_user = ""; static $admin_user = "";
// miniumum required length of user password /**
* @var int Miniumum required length of user password
*/
static $pass_lenmin = 12; static $pass_lenmin = 12;
// required strength of user password /**
// 0 = no requirements, * @var int Required strength of user password
// 1 = require mixed case letters (lower and upper), * 0 = no requirements,
// 2 = require mixed case and numbers * 1 = require mixed case letters (lower and upper),
// 3 = require mixed case, numbers and non-alphanumeric characters * 2 = require mixed case and numbers
* 3 = require mixed case, numbers and non-alphanumeric characters
*/
static $pass_strength = 2; static $pass_strength = 2;
// Default interval in seconds for live auto reload /**
* @var int Default interval in seconds for live auto reload
*/
static $interval = 10; static $interval = 10;
// Default language /**
* @var string Default language code
*/
static $lang = "en"; static $lang = "en";
// units /**
* @var string Default units
*/
static $units = "metric"; static $units = "metric";
/**
* @var int Stroke weight
*/
static $strokeWeight = 2; static $strokeWeight = 2;
/**
* @var string Stroke color
*/
static $strokeColor = '#ff0000'; static $strokeColor = '#ff0000';
/**
* @var int Stroke opacity
*/
static $strokeOpacity = 1; static $strokeOpacity = 1;
private static $fileLoaded = false; private static $fileLoaded = false;
@ -109,7 +158,7 @@
include_once($configFile); include_once($configFile);
if (isset($mapapi)) { self::$mapapi = $mapapi; } if (isset($mapapi)) { self::$mapapi = $mapapi; }
if (isset($gkey)) { self::$gkey = $gkey; } if (isset($gkey) && !empty($gkey)) { self::$gkey = $gkey; }
if (isset($ol_layers)) { self::$ol_layers = $ol_layers; } if (isset($ol_layers)) { self::$ol_layers = $ol_layers; }
if (isset($init_latitude)) { self::$init_latitude = $init_latitude; } if (isset($init_latitude)) { self::$init_latitude = $init_latitude; }
if (isset($init_longitude)) { self::$init_longitude = $init_longitude; } if (isset($init_longitude)) { self::$init_longitude = $init_longitude; }

144
index.php
View File

@ -73,51 +73,79 @@
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="<?= uConfig::$lang ?>">
<head> <head>
<title><?= $lang["title"] ?></title> <title><?= $lang["title"] ?></title>
<?php include("meta.php"); ?> <?php include("meta.php"); ?>
<script> <script>
var interval = '<?= uConfig::$interval ?>'; /** @namespace uLogger */
var userid = '<?= ($displayUserId) ? $displayUserId : -1 ?>'; var uLogger = window.uLogger || {};
var trackid = '<?= ($displayTrackId) ? $displayTrackId : -1 ?>'; /** @type {number} userId */
var units = '<?= uConfig::$units ?>'; uLogger.userId = <?= json_encode($displayUserId ? $displayUserId : -1) ?>;
var mapapi = '<?= uConfig::$mapapi ?>'; /** @type {number} trackId */
var gkey = '<?= !empty(uConfig::$gkey) ? uConfig::$gkey : "null" ?>'; uLogger.trackId = <?= json_encode($displayTrackId ? $displayTrackId : -1) ?>;
var ol_layers = <?= json_encode(uConfig::$ol_layers) ?>;
var init_latitude = <?= uConfig::$init_latitude ?>; /** @type {uLogger.config} */
var init_longitude = <?= uConfig::$init_longitude ?>; uLogger.config = {
var lang = <?= json_encode($lang) ?>; /** @type {number} */
var admin = <?= json_encode($auth->isAdmin()) ?>; interval: <?= json_encode(uConfig::$interval) ?>,
var auth = '<?= ($auth->isAuthenticated()) ? $auth->user->login : "null" ?>'; /** @type {string} */
var pass_regex = <?= uConfig::passRegex() ?>; units: <?= json_encode(uConfig::$units) ?>,
var strokeWeight = <?= uConfig::$strokeWeight ?>; /** @type {string} */
var strokeColor = '<?= uConfig::$strokeColor ?>'; mapapi: <?= json_encode(uConfig::$mapapi) ?>,
var strokeOpacity = <?= uConfig::$strokeOpacity ?>; /** @type {?string} */
gkey: <?= json_encode(uConfig::$gkey) ?>,
/** @type {Object.<string, string>} */
ol_layers: <?= json_encode(uConfig::$ol_layers) ?>,
/** @type {number} */
init_latitude: <?= json_encode(uConfig::$init_latitude) ?>,
/** @type {number} */
init_longitude: <?= json_encode(uConfig::$init_longitude) ?>,
/** @type {boolean} */
admin: <?= json_encode($auth->isAdmin()) ?>,
/** @type {?string} */
auth: <?= json_encode($auth->isAuthenticated() ? $auth->user->login : NULL) ?>,
/** @type {RegExp} */
pass_regex: <?= uConfig::passRegex() ?>,
/** @type {number} */
strokeWeight: <?= json_encode(uConfig::$strokeWeight) ?>,
/** @type {string} */
strokeColor: <?= json_encode(uConfig::$strokeColor) ?>,
/** @type {number} */
strokeOpacity: <?= json_encode(uConfig::$strokeOpacity) ?>
};
/** @type {uLogger.lang} */
uLogger.lang = {
/** @type {Object.<string, string>} */
strings: <?= json_encode($lang) ?>
};
</script> </script>
<script type="text/javascript" src="js/main.js"></script> <script src="js/main.js"></script>
<?php if ($auth->isAdmin()): ?> <?php if ($auth->isAdmin()): ?>
<script type="text/javascript" src="js/admin.js"></script> <script src="js/admin.js"></script>
<?php endif; ?> <?php endif; ?>
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<script type="text/javascript" src="js/track.js"></script> <script src="js/track.js"></script>
<?php endif; ?> <?php endif; ?>
<script type="text/javascript" src="js/pass.js"></script> <script src="js/pass.js"></script>
<script type="text/javascript" src="//www.google.com/jsapi"></script> <script src="js/api_gmaps.js"></script>
<script type="text/javascript"> <script src="js/api_openlayers.js"></script>
<script src="//www.google.com/jsapi"></script>
<script>
google.load('visualization', '1', { packages:['corechart'] }); google.load('visualization', '1', { packages:['corechart'] });
</script> </script>
</head> </head>
<body onload="loadMapAPI();"> <body>
<div id="menu"> <div id="menu">
<div id="menu-content"> <div id="menu-content">
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<div id="user_menu"> <div id="user_menu">
<a href="javascript:void(0);" onclick="userMenu()"><img class="icon" alt="<?= $lang["user"] ?>" src="images/user.svg"> <?= htmlspecialchars($auth->user->login) ?></a> <a id="menu_head"><img class="icon" alt="<?= $lang["user"] ?>" src="images/user.svg"> <?= htmlspecialchars($auth->user->login) ?></a>
<div id="user_dropdown" class="dropdown"> <div id="user_dropdown" class="dropdown">
<a href="javascript:void(0)" onclick="changePass()"><img class="icon" alt="<?= $lang["changepass"] ?>" src="images/lock.svg"> <?= $lang["changepass"] ?></a> <a id="menu_pass"><img class="icon" alt="<?= $lang["changepass"] ?>" src="images/lock.svg"> <?= $lang["changepass"] ?></a>
<a href="utils/logout.php"><img class="icon" alt="<?= $lang["logout"] ?>" src="images/poweroff.svg"> <?= $lang["logout"] ?></a> <a href="utils/logout.php"><img class="icon" alt="<?= $lang["logout"] ?>" src="images/poweroff.svg"> <?= $lang["logout"] ?></a>
</div> </div>
</div> </div>
@ -125,11 +153,11 @@
<a href="login.php"><img class="icon" alt="<?= $lang["login"] ?>" src="images/key.svg"> <?= $lang["login"] ?></a> <a href="login.php"><img class="icon" alt="<?= $lang["login"] ?>" src="images/key.svg"> <?= $lang["login"] ?></a>
<?php endif; ?> <?php endif; ?>
<div id="user"> <div class="section">
<?php if (!empty($usersArr)): ?> <?php if (!empty($usersArr)): ?>
<div class="menutitle" style="padding-top: 1em"><?= $lang["user"] ?></div> <label for="user" class="menutitle"><?= $lang["user"] ?></label>
<form> <form>
<select name="user" onchange="selectUser(this);"> <select id="user" name="user">
<option value="0" disabled><?= $lang["suser"] ?></option> <option value="0" disabled><?= $lang["suser"] ?></option>
<?php foreach ($usersArr as $aUser): ?> <?php foreach ($usersArr as $aUser): ?>
<option <?= ($aUser->id == $displayUserId) ? "selected " : "" ?>value="<?= $aUser->id ?>"><?= htmlspecialchars($aUser->login) ?></option> <option <?= ($aUser->id == $displayUserId) ? "selected " : "" ?>value="<?= $aUser->id ?>"><?= htmlspecialchars($aUser->login) ?></option>
@ -139,40 +167,40 @@
<?php endif; ?> <?php endif; ?>
</div> </div>
<div id="track"> <div class="section">
<div class="menutitle"><?= $lang["track"] ?></div> <label for="track" class="menutitle"><?= $lang["track"] ?></label>
<form> <form>
<select name="track" onchange="selectTrack(this)"> <select id="track" name="track">
<?php foreach ($tracksArr as $aTrack): ?> <?php foreach ($tracksArr as $aTrack): ?>
<option value="<?= $aTrack->id ?>"><?= htmlspecialchars($aTrack->name) ?></option> <option value="<?= $aTrack->id ?>"><?= htmlspecialchars($aTrack->name) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<input id="latest" type="checkbox" onchange="toggleLatest();"> <?= $lang["latest"] ?><br> <input id="latest" type="checkbox"> <label for="latest"><?= $lang["latest"] ?></label><br>
<input type="checkbox" onchange="autoReload();"> <?= $lang["autoreload"] ?> (<a href="javascript:void(0);" onclick="setTime();"><span id="auto"><?= uConfig::$interval ?></span></a> s)<br> <input id="auto_reload" type="checkbox"> <label for="auto_reload"><?= $lang["autoreload"] ?></label> (<a id="set_time"><span id="auto"><?= uConfig::$interval ?></span></a> s)<br>
</form> </form>
<a href="javascript:void(0);" onclick="reload(userid, trackid);"> <?= $lang["reload"] ?></a><br> <a id="force_reload"> <?= $lang["reload"] ?></a><br>
</div> </div>
<div id="summary"></div> <div id="summary" class="section"></div>
<div id="other"> <div id="other" class="section">
<a id="altitudes" href="javascript:void(0);" onclick="toggleChart();"><?= $lang["chart"] ?></a> <a id="altitudes"><?= $lang["chart"] ?></a>
</div> </div>
<div id="api"> <div>
<div class="menutitle"><?= $lang["api"] ?></div> <label for="api" class="menutitle"><?= $lang["api"] ?></label>
<form> <form>
<select name="api" onchange="loadMapAPI(this.options[this.selectedIndex].value);"> <select id="api" name="api">
<option value="gmaps"<?= (uConfig::$mapapi == "gmaps") ? " selected" : "" ?>>Google Maps</option> <option value="gmaps"<?= (uConfig::$mapapi == "gmaps") ? " selected" : "" ?>>Google Maps</option>
<option value="openlayers"<?= (uConfig::$mapapi == "openlayers") ? " selected" : "" ?>>OpenLayers</option> <option value="openlayers"<?= (uConfig::$mapapi == "openlayers") ? " selected" : "" ?>>OpenLayers</option>
</select> </select>
</form> </form>
</div> </div>
<div id="lang"> <div>
<div class="menutitle"><?= $lang["language"] ?></div> <label for="lang" class="menutitle"><?= $lang["language"] ?></label>
<form> <form>
<select name="units" onchange="setLang(this.options[this.selectedIndex].value);"> <select id="lang" name="lang">
<?php foreach ($langsArr as $langCode => $langName): ?> <?php foreach ($langsArr as $langCode => $langName): ?>
<option value="<?= $langCode ?>"<?= (uConfig::$lang == $langCode) ? " selected" : "" ?>><?= $langName ?></option> <option value="<?= $langCode ?>"<?= (uConfig::$lang == $langCode) ? " selected" : "" ?>><?= $langName ?></option>
<?php endforeach; ?> <?php endforeach; ?>
@ -180,10 +208,10 @@
</form> </form>
</div> </div>
<div id="units"> <div class="section">
<div class="menutitle"><?= $lang["units"] ?></div> <label for="units" class="menutitle"><?= $lang["units"] ?></label>
<form> <form>
<select name="units" onchange="setUnits(this.options[this.selectedIndex].value);"> <select id="units" name="units">
<option value="metric"<?= (uConfig::$units == "metric") ? " selected" : "" ?>><?= $lang["metric"] ?></option> <option value="metric"<?= (uConfig::$units == "metric") ? " selected" : "" ?>><?= $lang["metric"] ?></option>
<option value="imperial"<?= (uConfig::$units == "imperial") ? " selected" : "" ?>><?= $lang["imperial"] ?></option> <option value="imperial"<?= (uConfig::$units == "imperial") ? " selected" : "" ?>><?= $lang["imperial"] ?></option>
<option value="nautical"<?= (uConfig::$units == "nautical") ? " selected" : "" ?>><?= $lang["nautical"] ?></option> <option value="nautical"<?= (uConfig::$units == "nautical") ? " selected" : "" ?>><?= $lang["nautical"] ?></option>
@ -191,34 +219,34 @@
</form> </form>
</div> </div>
<div id="export"> <div id="export" class="section">
<div class="menutitle u"><?= $lang["export"] ?></div> <div class="menutitle u"><?= $lang["export"] ?></div>
<a class="menulink" href="javascript:void(0);" onclick="exportFile('kml', userid, trackid);">kml</a> <a id="export_kml" class="menulink">kml</a>
<a class="menulink" href="javascript:void(0);" onclick="exportFile('gpx', userid, trackid);">gpx</a> <a id="export_gpx" class="menulink">gpx</a>
</div> </div>
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<div id="import"> <div id="import" class="section">
<div class="menutitle u"><?= $lang["import"] ?></div> <div class="menutitle u"><?= $lang["import"] ?></div>
<form id="importForm" enctype="multipart/form-data" method="post"> <form id="importForm" enctype="multipart/form-data" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="<?= uUtils::getUploadMaxSize() ?>" /> <input type="hidden" name="MAX_FILE_SIZE" value="<?= uUtils::getUploadMaxSize() ?>" />
<input type="file" id="inputFile" name="gpx" style="display:none" onchange="importFile(this)" /> <input type="file" id="inputFile" name="gpx" />
</form> </form>
<a class="menulink" href="javascript:void(0);" onclick="document.getElementById('inputFile').click();">gpx</a> <a id="import_gpx" class="menulink">gpx</a>
</div> </div>
<div id="admin_menu"> <div id="admin_menu">
<div class="menutitle u"><?= $lang["adminmenu"] ?></div> <div class="menutitle u"><?= $lang["adminmenu"] ?></div>
<?php if ($auth->isAdmin()): ?> <?php if ($auth->isAdmin()): ?>
<a class="menulink" href="javascript:void(0);" onclick="addUser()"><?= $lang["adduser"] ?></a> <a id="adduser" class="menulink"><?= $lang["adduser"] ?></a>
<a class="menulink" href="javascript:void(0);" onclick="editUser()"><?= $lang["edituser"] ?></a> <a id="edituser" class="menulink"><?= $lang["edituser"] ?></a>
<?php endif; ?> <?php endif; ?>
<a class="menulink" href="javascript:void(0);" onclick="editTrack()"><?= $lang["edittrack"] ?></a> <a id="edittrack" class="menulink"><?= $lang["edittrack"] ?></a>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div id="menu-close" onclick="toggleMenu();">»</div> <div id="menu-close">»</div>
<div id="footer"><a target="_blank" href="https://github.com/bfabiszewski/ulogger-server"><span class="mi">μ</span>logger</a> <?= uConfig::$version ?></div> <div id="footer"><a target="_blank" href="https://github.com/bfabiszewski/ulogger-server"><span class="mi">μ</span>logger</a> <?= uConfig::$version ?></div>
</div> </div>
@ -226,7 +254,7 @@
<div id="map-canvas"></div> <div id="map-canvas"></div>
<div id="bottom"> <div id="bottom">
<div id="chart"></div> <div id="chart"></div>
<div id="close"><a href="javascript:void(0);" onclick="toggleChart(0);"><?= $lang["close"] ?></a></div> <div id="chart_close"><?= $lang["close"] ?></div>
</div> </div>
</div> </div>

View File

@ -16,104 +16,126 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
function addUser() { /** @namespace */
var form = '<form id="userForm" method="post" onsubmit="submitUser(\'add\'); return false">'; var uLogger = window.uLogger || {};
form += '<label><b>' + lang['username'] + '</b></label><input type="text" placeholder="' + lang['usernameenter'] + '" name="login" required>'; (function (ul) {
form += '<label><b>' + lang['password'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass" required>';
form += '<label><b>' + lang['passwordrepeat'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass2" required>';
form += '<div class="buttons"><button type="button" onclick="removeModal()">' + lang['cancel'] + '</button><button type="submit">' + lang['submit'] + '</button></div>';
form += '</form>';
showModal(form);
}
function editUser() { /**
var userForm = document.getElementsByName('user')[0]; * @typedef uLogger.admin
var userLogin = (userForm !== undefined) ? userForm.options[userForm.selectedIndex].text : auth; * @memberOf uLogger
if (userLogin == auth) { * @type {Object}
alert(lang['selfeditwarn']); * @property {function} addUser
* @property {function} editUser
* @property {function} submitUser
*/
ul.admin = (function (ns) {
/**
* Show add user dialog
*/
function addUser() {
var form = '<form id="userForm" method="post" onsubmit="uLogger.admin.submitUser(\'add\'); return false">';
form += '<label><b>' + ns.lang.strings['username'] + '</b></label><input type="text" placeholder="' + ns.lang.strings['usernameenter'] + '" name="login" required>';
form += '<label><b>' + ns.lang.strings['password'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass" required>';
form += '<label><b>' + ns.lang.strings['passwordrepeat'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass2" required>';
form += '<div class="buttons"><button type="button" onclick="uLogger.ui.removeModal()">' + ns.lang.strings['cancel'] + '</button><button type="submit">' + ns.lang.strings['submit'] + '</button></div>';
form += '</form>';
ns.ui.showModal(form);
}
/**
* Show edit user dialog
*/
function editUser() {
var userForm = ns.ui.userSelect;
var userLogin = (userForm) ? userForm.options[userForm.selectedIndex].text : ns.config.auth;
if (userLogin === ns.config.auth) {
alert(ns.lang.strings['selfeditwarn']);
return; return;
} }
var message = '<div style="float:left">' + sprintf(lang['editinguser'], '<b>' + htmlEncode(userLogin) + '</b>') + '</div>'; var message = '<div style="float:left">' + ns.sprintf(ns.lang.strings['editinguser'], '<b>' + ns.htmlEncode(userLogin) + '</b>') + '</div>';
message += '<div class="red-button"><b><a href="javascript:void(0);" onclick="submitUser(\'delete\'); return false">' + lang['deluser'] + '</a></b></div>'; message += '<div class="red-button"><b><a href="javascript:void(0);" onclick="uLogger.admin.submitUser(\'delete\'); return false">' + ns.lang.strings['deluser'] + '</a></b></div>';
message += '<div style="clear: both; padding-bottom: 1em;"></div>'; message += '<div style="clear: both; padding-bottom: 1em;"></div>';
var form = '<form id="userForm" method="post" onsubmit="submitUser(\'update\'); return false">'; var form = '<form id="userForm" method="post" onsubmit="uLogger.admin.submitUser(\'update\'); return false">';
form += '<input type="hidden" name="login" value="' + htmlEncode(userLogin) + '">'; form += '<input type="hidden" name="login" value="' + ns.htmlEncode(userLogin) + '">';
form += '<label><b>' + lang['password'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass" required>'; form += '<label><b>' + ns.lang.strings['password'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass" required>';
form += '<label><b>' + lang['passwordrepeat'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass2" required>'; form += '<label><b>' + ns.lang.strings['passwordrepeat'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass2" required>';
form += '<div class="buttons"><button type="button" onclick="removeModal()">' + lang['cancel'] + '</button><button type="submit">' + lang['submit'] + '</button></div>'; form += '<div class="buttons"><button type="button" onclick="uLogger.ui.removeModal()">' + ns.lang.strings['cancel'] + '</button><button type="submit">' + ns.lang.strings['submit'] + '</button></div>';
form += '</form>'; form += '</form>';
showModal(message + form); ns.ui.showModal(message + form);
} }
function confirmedDelete(login) { /**
return confirm(sprintf(lang['userdelwarn'], '"' + login + '"')); * Show confirmation dialog
} * @param {string} login
* @returns {boolean} True if confirmed
*/
function confirmedDelete(login) {
return confirm(ns.sprintf(ns.lang.strings['userdelwarn'], '"' + login + '"'));
}
function submitUser(action) { /**
* Submit user form
* @param {string} action Add, delete, update
*/
function submitUser(action) {
var form = document.getElementById('userForm'); var form = document.getElementById('userForm');
var login = form.elements['login'].value.trim(); var login = form.elements['login'].value.trim();
if (!login) { if (!login) {
alert(lang['allrequired']); alert(ns.lang.strings['allrequired']);
return; return;
} }
var pass = null; var pass = null;
var pass2 = null; var pass2 = null;
if (action != 'delete') { if (action !== 'delete') {
pass = form.elements['pass'].value; pass = form.elements['pass'].value;
pass2 = form.elements['pass2'].value; pass2 = form.elements['pass2'].value;
if (!pass || !pass2) { if (!pass || !pass2) {
alert(lang['allrequired']); alert(ns.lang.strings['allrequired']);
return; return;
} }
if (pass != pass2) { if (pass !== pass2) {
alert(lang['passnotmatch']); alert(ns.lang.strings['passnotmatch']);
return; return;
} }
if (!pass_regex.test(pass)) { if (!ns.config.pass_regex.test(pass)) {
alert(lang['passlenmin'] + '\n' + lang['passrules']); alert(ns.lang.strings['passlenmin'] + '\n' + ns.lang.strings['passrules']);
return; return;
} }
} else { } else if (!confirmedDelete(login)) {
if (!confirmedDelete(login)) {
return; return;
} }
}
var xhr = getXHR(); ns.post('utils/handleuser.php',
xhr.onreadystatechange = function() { {
if (xhr.readyState == 4) { action: action,
var error = true; login: login,
var message = ''; pass: pass
if (xhr.status == 200) { },
var xml = xhr.responseXML; {
if (xml) { success: function () {
var root = xml.getElementsByTagName('root'); ns.ui.removeModal();
if (root.length && getNode(root[0], 'error') == 0) { alert(ns.lang.strings['actionsuccess']);
removeModal(); if (action === 'delete') {
alert(lang['actionsuccess']); var f = ns.ui.userSelect;
if (action == 'delete') {
// select current user in users form
var f = document.getElementsByName('user')[0];
f.remove(f.selectedIndex); f.remove(f.selectedIndex);
selectUser(f); ns.selectUser(f);
} }
error = false; },
} else if (root.length) { fail: function (message) {
errorMsg = getNode(root[0], 'message'); alert(ns.lang.strings['actionfailure'] + '\n' + message);
if (errorMsg) { message = errorMsg; }
} }
});
} }
// noinspection JSUnusedGlobalSymbols
return {
addUser: addUser,
editUser: editUser,
submitUser: submitUser
} }
if (error) {
alert(lang['actionfailure'] + '\n' + message); })(ul);
}
xhr = null; })(uLogger);
}
}
xhr.open('POST', 'utils/handleuser.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
var params = 'action=' + action + '&login=' + encodeURIComponent(login) + '&pass=' + encodeURIComponent(pass);
params = params.replace(/%20/g, '+');
xhr.send(params);
return;
}

View File

@ -17,44 +17,90 @@
*/ */
// google maps // google maps
var map; /** @namespace */
var polies = []; var uLogger = uLogger || {};
var markers = []; /** @namespace */
var popups = []; uLogger.mapAPI = uLogger.mapAPI || {};
var popup; /** @namespace */
var polyOptions; uLogger.mapAPI.gmaps = (function(ns) {
var mapOptions;
var loadedAPI = 'gmaps';
function init() { /** @type {google.maps.Map} */
if (gm_error) { return gm_authFailure(); } var map;
google.maps.visualRefresh = true; /** @type {google.maps.Polyline[]} */
polyOptions = { var polies = [];
strokeColor: strokeColor, /** @type {google.maps.Marker[]} */
strokeOpacity: strokeOpacity, var markers = [];
strokeWeight: strokeWeight /** @type {google.maps.InfoWindow[]} */
var popups = [];
/** @type {google.maps.InfoWindow} */
var popup;
/** @type {google.maps.PolylineOptions} */
var polyOptions;
/** @type {google.maps.MapOptions} */
var mapOptions;
/** @type {number} */
var timeoutHandle;
var name = 'gmaps';
var isLoaded = false;
var authError = false;
/**
* Initialize map
*/
function init() {
var url = '//maps.googleapis.com/maps/api/js?' + ((ns.config.gkey != null) ? ('key=' + ns.config.gkey + '&') : '') + 'callback=uLogger.mapAPI.gmaps.setLoaded';
ns.addScript(url, 'mapapi_gmaps');
if (!isLoaded) {
throw new Error("Google Maps API not ready");
} }
start();
}
/**
* Start map engine when loaded
*/
function start() {
if (authError) {
gm_authFailure();
return;
}
google.maps.visualRefresh = true;
// noinspection JSValidateTypes
polyOptions = {
strokeColor: ns.config.strokeColor,
strokeOpacity: ns.config.strokeOpacity,
strokeWeight: ns.config.strokeWeight
};
// noinspection JSValidateTypes
mapOptions = { mapOptions = {
center: new google.maps.LatLng(init_latitude, init_longitude), center: new google.maps.LatLng(ns.config.init_latitude, ns.config.init_longitude),
zoom: 8, zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP, mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: true scaleControl: true
}; };
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); map = new google.maps.Map(ns.ui.map, mapOptions);
} }
function cleanup() {
map = undefined;
polies = undefined;
markers = undefined;
popups = undefined;
popup = undefined;
polyOptions = undefined;
mapOptions = undefined;
document.getElementById('map-canvas').innerHTML = '';
}
function displayTrack(xml, update) { /**
altitudes = {}; * Clean up API
*/
function cleanup() {
polies = [];
markers = [];
popups = [];
map = null;
polyOptions = null;
mapOptions = null;
popup = null;
ns.clearMapCanvas();
}
/**
* Display track
* @param {HTMLCollection} positions XML element
* @param {boolean} update Should fit bounds if true
*/
function displayTrack(positions, update) {
var totalMeters = 0; var totalMeters = 0;
var totalSeconds = 0; var totalSeconds = 0;
// init polyline // init polyline
@ -62,27 +108,26 @@ function displayTrack(xml, update) {
poly.setMap(map); poly.setMap(map);
var path = poly.getPath(); var path = poly.getPath();
var latlngbounds = new google.maps.LatLngBounds(); var latlngbounds = new google.maps.LatLngBounds();
var positions = xml.getElementsByTagName('position');
var posLen = positions.length; var posLen = positions.length;
for (var i = 0; i < posLen; i++) { for (var i = 0; i < posLen; i++) {
var p = parsePosition(positions[i], i); var p = ns.parsePosition(positions[i], i);
totalMeters += p.distance; totalMeters += p.distance;
totalSeconds += p.seconds; totalSeconds += p.seconds;
p.totalMeters = totalMeters; p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds; p.totalSeconds = totalSeconds;
p.coordinates = new google.maps.LatLng(p.latitude, p.longitude);
// set marker // set marker
setMarker(p, i, posLen); setMarker(p, i, posLen);
// update polyline // update polyline
path.push(p.coordinates); var coordinates = new google.maps.LatLng(p.latitude, p.longitude);
latlngbounds.extend(p.coordinates); path.push(coordinates);
latlngbounds.extend(coordinates);
} }
if (update) { if (update) {
map.fitBounds(latlngbounds); map.fitBounds(latlngbounds);
if (i == 1) { if (i === 1) {
// only one point, zoom out // only one point, zoom out
zListener = var zListener =
google.maps.event.addListenerOnce(map, 'bounds_changed', function (event) { google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
if (this.getZoom()) { if (this.getZoom()) {
this.setZoom(15); this.setZoom(15);
} }
@ -92,117 +137,160 @@ function displayTrack(xml, update) {
} }
polies.push(poly); polies.push(poly);
updateSummary(p.timestamp, totalMeters, totalSeconds); ns.updateSummary(p.timestamp, totalMeters, totalSeconds);
if (p.tid != trackid) { if (p.tid !== ns.config.trackid) {
trackid = p.tid; ns.config.trackid = p.tid;
setTrack(trackid); ns.setTrack(ns.config.trackid);
} }
if (document.getElementById('bottom').style.display == 'block') { ns.updateChart();
// update altitudes chart
chart.clearChart();
displayChart();
} }
}
function clearMap() { /**
* Clear map
*/
function clearMap() {
if (polies) { if (polies) {
for (var i = 0; i < polies.length; i++) { for (var i = 0; i < polies.length; i++) {
polies[i].setMap(null); polies[i].setMap(null);
} }
} }
if (markers) { if (markers) {
for (var i = 0; i < markers.length; i++) { for (var j = 0; j < markers.length; j++) {
google.maps.event.removeListener(popups[i].listener); google.maps.event.removeListener(popups[j].listener);
popups[i].setMap(null); popups[j].setMap(null);
markers[i].setMap(null); markers[j].setMap(null);
} }
} }
markers.length = 0; markers.length = 0;
polies.length = 0; polies.length = 0;
popups.lentgth = 0; popups.lentgth = 0;
} }
function setMarker(p, i, posLen) { /**
* Set marker
* @param {uLogger.Position} pos
* @param {number} id
* @param {number} posLen
*/
function setMarker(pos, id, posLen) {
// marker // marker
// noinspection JSCheckFunctionSignatures
var marker = new google.maps.Marker({ var marker = new google.maps.Marker({
map: map, position: new google.maps.LatLng(pos.latitude, pos.longitude),
position: new google.maps.LatLng(p.latitude, p.longitude), title: (new Date(pos.timestamp * 1000)).toLocaleString(),
title: (new Date(p.timestamp * 1000)).toLocaleString() map: map
}); });
if (latest == 1) { marker.setIcon('images/marker-red.png') } if (ns.isLatest()) {
else if (i == 0) { marker.setIcon('images/marker-green.png') } marker.setIcon('images/marker-red.png');
else if (i == posLen - 1) { marker.setIcon('images/marker-red.png') } } else if (id === 0) {
else { marker.setIcon('images/marker-white.png') } marker.setIcon('images/marker-green.png');
} else if (id === posLen - 1) {
marker.setIcon('images/marker-red.png');
} else {
marker.setIcon('images/marker-white.png');
}
// popup // popup
var content = getPopupHtml(p, i, posLen); var content = ns.getPopupHtml(pos, id, posLen);
popup = new google.maps.InfoWindow(); popup = new google.maps.InfoWindow();
popup.listener = google.maps.event.addListener(marker, 'click', (function (marker, content) { // noinspection JSUndefinedPropertyAssignment
popup.listener = google.maps.event.addListener(marker, 'click', (function (_marker, _content) {
return function () { return function () {
popup.setContent(content); popup.setContent(_content);
popup.open(map, marker); popup.open(map, _marker);
if (document.getElementById('bottom').style.display == 'block') { ns.chartShowPosition(id);
var index = 0;
for (var key in altitudes) {
if (altitudes.hasOwnProperty(key) && key == i) {
chart.setSelection([{ row: index, column: null }]);
break;
}
index++;
}
}
} }
})(marker, content)); })(marker, content));
markers.push(marker); markers.push(marker);
popups.push(popup); popups.push(popup);
} }
function addChartEvent(chart, data) { /**
* Add listener on chart to show position on map
* @param {google.visualization.LineChart} chart
* @param {google.visualization.DataTable} data
*/
function addChartEvent(chart, data) {
google.visualization.events.addListener(chart, 'select', function () { google.visualization.events.addListener(chart, 'select', function () {
if (popup) { popup.close(); clearTimeout(altTimeout); } if (popup) { popup.close(); clearTimeout(timeoutHandle); }
var selection = chart.getSelection()[0]; var selection = chart.getSelection()[0];
if (selection) { if (selection) {
var id = data.getValue(selection.row, 0) - 1; var id = data.getValue(selection.row, 0) - 1;
var icon = markers[id].getIcon(); var icon = markers[id].getIcon();
markers[id].setIcon('images/marker-gold.png'); markers[id].setIcon('images/marker-gold.png');
altTimeout = setTimeout(function () { markers[id].setIcon(icon); }, 2000); timeoutHandle = setTimeout(function () { markers[id].setIcon(icon); }, 2000);
} }
}); });
} }
//((52.20105108685229, 20.789387865580238), (52.292069558807135, 21.172192736185707)) /**
function getBounds() { * Get map bounds
* eg. ((52.20105108685229, 20.789387865580238), (52.292069558807135, 21.172192736185707))
* @returns {number[]} Bounds
*/
function getBounds() {
var bounds = map.getBounds(); var bounds = map.getBounds();
var lat_sw = bounds.getSouthWest().lat(); var lat_sw = bounds.getSouthWest().lat();
var lon_sw = bounds.getSouthWest().lng(); var lon_sw = bounds.getSouthWest().lng();
var lat_ne = bounds.getNorthEast().lat(); var lat_ne = bounds.getNorthEast().lat();
var lon_ne = bounds.getNorthEast().lng(); var lon_ne = bounds.getNorthEast().lng();
return [lon_sw, lat_sw, lon_ne, lat_ne]; return [lon_sw, lat_sw, lon_ne, lat_ne];
} }
function zoomToExtent() { /**
* Zoom to track extent
*/
function zoomToExtent() {
var latlngbounds = new google.maps.LatLngBounds(); var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; i++) { for (var i = 0; i < markers.length; i++) {
var coordinates = new google.maps.LatLng(markers[i].position.lat(), markers[i].position.lng()); var coordinates = new google.maps.LatLng(markers[i].position.lat(), markers[i].position.lng());
latlngbounds.extend(coordinates); latlngbounds.extend(coordinates);
} }
map.fitBounds(latlngbounds); map.fitBounds(latlngbounds);
} }
function zoomToBounds(b) { /**
var sw = new google.maps.LatLng(b[1], b[0]); * Zoom to bounds
var ne = new google.maps.LatLng(b[3], b[2]); * @param {number[]} bounds
var bounds = new google.maps.LatLngBounds(sw, ne); */
map.fitBounds(bounds); function zoomToBounds(bounds) {
} var sw = new google.maps.LatLng(bounds[1], bounds[0]);
var ne = new google.maps.LatLng(bounds[3], bounds[2]);
var latLngBounds = new google.maps.LatLngBounds(sw, ne);
map.fitBounds(latLngBounds);
}
/**
* Update size
*/
function updateSize() {
// ignore for google API
}
return {
name: name,
init: init,
setLoaded: function () { isLoaded = true; },
cleanup: cleanup,
displayTrack: displayTrack,
clearMap: clearMap,
setMarker: setMarker,
addChartEvent: addChartEvent,
getBounds: getBounds,
zoomToExtent: zoomToExtent,
zoomToBounds: zoomToBounds,
updateSize: updateSize
}
})(uLogger);
/**
* Callback for Google Maps API
* It will be called when authentication fails
*/
function gm_authFailure() { function gm_authFailure() {
gm_error = true; uLogger.mapAPI.gmaps.authError = true;
message = sprintf(lang['apifailure'], 'Google Maps'); var message = uLogger.sprintf(uLogger.lang.strings['apifailure'], 'Google Maps');
message += '<br><br>' + lang['gmauthfailure']; message += '<br><br>' + uLogger.lang.strings['gmauthfailure'];
message += '<br><br>' + lang['gmapilink']; message += '<br><br>' + uLogger.lang.strings['gmapilink'];
showModal(message); uLogger.ui.showModal(message);
};
function updateSize() {
// ignore
} }

View File

@ -16,27 +16,49 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
// openlayers 3+
var map;
var layerTrack;
var layerMarkers;
var selectedLayer;
var olStyles;
var loadedAPI = 'openlayers';
function init() { // openlayers 3+
/** @namespace */
var uLogger = uLogger || {};
/** @namespace */
uLogger.mapAPI = uLogger.mapAPI || {};
/** @namespace */
uLogger.mapAPI.ol = (function(ns) {
addCss('css/ol.css', 'ol_css'); /** @type {ol.Map} */
var map;
/** @type {ol.layer.Vector} */
var layerTrack;
/** @type {ol.layer.Vector} */
var layerMarkers;
/** @type {ol.layer.Base} */
var selectedLayer;
/** @type {ol.style.Style|{}} */
var olStyles;
var name = 'openlayers';
/**
* Initialize map
*/
function init() {
var urls = [];
urls.push('//cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList');
urls.push('js/ol.js');
for (var i = 0; i < urls.length; i++) {
ns.addScript(urls[i], 'mapapi_openlayers' + '_' + i);
}
ns.addCss('css/ol.css', 'ol_css');
var controls = [ var controls = [
new ol.control.Zoom(), new ol.control.Zoom(),
new ol.control.Rotate(), new ol.control.Rotate(),
new ol.control.ScaleLine(), new ol.control.ScaleLine(),
new ol.control.ZoomToExtent({ label: getExtentImg() }), new ol.control.ZoomToExtent({label: getExtentImg()})
]; ];
var view = new ol.View({ var view = new ol.View({
center: ol.proj.fromLonLat([init_longitude, init_latitude]), center: ol.proj.fromLonLat([ns.config.init_longitude, ns.config.init_latitude]),
zoom: 8 zoom: 8
}); });
@ -56,9 +78,9 @@ function init() {
selectedLayer = osm; selectedLayer = osm;
// add extra layers // add extra layers
for (var layerName in ol_layers) { for (var layerName in ns.config.ol_layers) {
if (ol_layers.hasOwnProperty(layerName)) { if (ns.config.ol_layers.hasOwnProperty(layerName)) {
var layerUrl = ol_layers[layerName]; var layerUrl = ns.config.ol_layers[layerName];
var ol_layer = new ol.layer.Tile({ var ol_layer = new ol.layer.Tile({
name: layerName, name: layerName,
visible: false, visible: false,
@ -73,8 +95,8 @@ function init() {
// init layers // init layers
var lineStyle = new ol.style.Style({ var lineStyle = new ol.style.Style({
stroke: new ol.style.Stroke({ stroke: new ol.style.Stroke({
color: hexToRGBA(strokeColor, strokeOpacity), color: ns.hexToRGBA(ns.config.strokeColor, ns.config.strokeOpacity),
width: strokeWeight width: ns.config.strokeWeight
}) })
}); });
layerTrack = new ol.layer.Vector({ layerTrack = new ol.layer.Vector({
@ -94,20 +116,20 @@ function init() {
// styles // styles
olStyles = {}; olStyles = {};
var iconRed = new ol.style.Icon({ var iconRed = new ol.style.Icon({
anchor: [ 0.5, 1 ], anchor: [0.5, 1],
src: 'images/marker-red.png' src: 'images/marker-red.png'
}); });
var iconGreen = new ol.style.Icon({ var iconGreen = new ol.style.Icon({
anchor: [ 0.5, 1 ], anchor: [0.5, 1],
src: 'images/marker-green.png' src: 'images/marker-green.png'
}); });
var iconWhite = new ol.style.Icon({ var iconWhite = new ol.style.Icon({
anchor: [ 0.5, 1 ], anchor: [0.5, 1],
opacity: 0.7, opacity: 0.7,
src: 'images/marker-white.png' src: 'images/marker-white.png'
}); });
var iconGold = new ol.style.Icon({ var iconGold = new ol.style.Icon({
anchor: [ 0.5, 1 ], anchor: [0.5, 1],
src: 'images/marker-gold.png' src: 'images/marker-gold.png'
}); });
olStyles['red'] = new ol.style.Style({ olStyles['red'] = new ol.style.Style({
@ -127,7 +149,7 @@ function init() {
var popupContainer = document.createElement('div'); var popupContainer = document.createElement('div');
popupContainer.id = 'popup'; popupContainer.id = 'popup';
popupContainer.className = 'ol-popup'; popupContainer.className = 'ol-popup';
document.getElementsByTagName('body')[0].appendChild(popupContainer); document.body.appendChild(popupContainer);
var popupCloser = document.createElement('a'); var popupCloser = document.createElement('a');
popupCloser.id = 'popup-closer'; popupCloser.id = 'popup-closer';
popupCloser.className = 'ol-popup-closer'; popupCloser.className = 'ol-popup-closer';
@ -145,53 +167,43 @@ function init() {
} }
}); });
popupCloser.onclick = function() { popupCloser.onclick = function () {
// eslint-disable-next-line no-undefined
popup.setPosition(undefined); popup.setPosition(undefined);
popupCloser.blur(); popupCloser.blur();
return false; return false;
}; };
// add click handler to map to show popup // add click handler to map to show popup
map.on('click', function(e) { map.on('click', function (e) {
var coordinate = e.coordinate; var coordinate = e.coordinate;
var feature = map.forEachFeatureAtPixel(e.pixel, var feature = map.forEachFeatureAtPixel(e.pixel,
function(feature, layer) { function (_feature, _layer) {
if (layer.get('name') == 'Markers') { if (_layer.get('name') === 'Markers') {
return feature; return _feature;
} }
return null;
}); });
if (feature) { if (feature) {
var p = feature.get('p'); var pos = feature.get('p');
var i = feature.getId(); var id = feature.getId();
var posLen = feature.get('posLen'); var posLen = feature.get('posLen');
// popup show // popup show
popup.setPosition(coordinate); popup.setPosition(coordinate);
popupContent.innerHTML = getPopupHtml(p, i, posLen); popupContent.innerHTML = ns.getPopupHtml(pos, id, posLen);
map.addOverlay(popup); map.addOverlay(popup);
if (document.getElementById('bottom').style.display == 'block') { ns.chartShowPosition(id);
var index = 0;
for (var key in altitudes) {
if (altitudes.hasOwnProperty(key) && key == i) {
chart.setSelection([{ row: index, column: null }]);
break;
}
index++;
}
}
} else { } else {
// popup destroy // popup destroy
// eslint-disable-next-line no-undefined
popup.setPosition(undefined); popup.setPosition(undefined);
} }
}); });
// change mouse cursor when over marker // change mouse cursor when over marker
map.on('pointermove', function(e) { map.on('pointermove', function (e) {
var hit = map.forEachFeatureAtPixel(e.pixel, function(feature, layer) { var hit = map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
if (layer.get('name') == 'Markers') { return layer.get('name') === 'Markers';
return true;
} else {
return false;
}
}); });
if (hit) { if (hit) {
this.getTargetElement().style.cursor = 'pointer'; this.getTargetElement().style.cursor = 'pointer';
@ -204,7 +216,7 @@ function init() {
var switcher = document.createElement('div'); var switcher = document.createElement('div');
switcher.id = 'switcher'; switcher.id = 'switcher';
switcher.className = 'ol-control'; switcher.className = 'ol-control';
document.getElementsByTagName('body')[0].appendChild(switcher); document.body.appendChild(switcher);
var switcherContent = document.createElement('div'); var switcherContent = document.createElement('div');
switcherContent.id = 'switcher-content'; switcherContent.id = 'switcher-content';
switcherContent.className = 'ol-layerswitcher'; switcherContent.className = 'ol-layerswitcher';
@ -232,9 +244,9 @@ function init() {
}); });
function switchLayer() { function switchLayer() {
var layerName = this.value; var targetName = this.value;
map.getLayers().forEach(function (layer) { map.getLayers().forEach(function (layer) {
if (layer.get('name') === layerName) { if (layer.get('name') === targetName) {
if (layer.get('type') === 'data') { if (layer.get('type') === 'data') {
if (layer.getVisible()) { if (layer.getVisible()) {
layer.setVisible(false); layer.setVisible(false);
@ -246,10 +258,9 @@ function init() {
selectedLayer = layer; selectedLayer = layer;
layer.setVisible(true); layer.setVisible(true);
} }
return;
} }
}); });
}; }
var switcherButton = document.createElement('button'); var switcherButton = document.createElement('button');
var layerImg = document.createElement('img'); var layerImg = document.createElement('img');
@ -257,7 +268,8 @@ function init() {
layerImg.style.width = '60%'; layerImg.style.width = '60%';
switcherButton.appendChild(layerImg); switcherButton.appendChild(layerImg);
var switcherHandle = function() { // eslint-disable-next-line func-style
var switcherHandle = function () {
var el = document.getElementById('switcher'); var el = document.getElementById('switcher');
if (el.style.display === 'block') { if (el.style.display === 'block') {
el.style.display = 'none'; el.style.display = 'none';
@ -277,29 +289,34 @@ function init() {
element: element element: element
}); });
map.addControl(switcherControl); map.addControl(switcherControl);
} }
function cleanup() { /**
map = undefined; * Clean up API
layerTrack = undefined; */
layerMarkers = undefined; function cleanup() {
selectedLayer = undefined; map = null;
olStyles = undefined; layerTrack = null;
removeElementById('popup'); layerMarkers = null;
removeElementById('switcher'); selectedLayer = null;
document.getElementById('map-canvas').innerHTML = ''; olStyles = null;
} ns.removeElementById('popup');
ns.removeElementById('switcher');
ns.clearMapCanvas();
}
/**
function displayTrack(xml, update) { * Display track
altitudes = {}; * @param {HTMLCollection} positions XML element
* @param {boolean} update Should fit bounds if true
*/
function displayTrack(positions, update) {
var totalMeters = 0; var totalMeters = 0;
var totalSeconds = 0; var totalSeconds = 0;
var points = []; var points = [];
var positions = xml.getElementsByTagName('position');
var posLen = positions.length; var posLen = positions.length;
for (var i = 0; i < posLen; i++) { for (var i = 0; i < posLen; i++) {
var p = parsePosition(positions[i], i); var p = ns.parsePosition(positions[i], i);
totalMeters += p.distance; totalMeters += p.distance;
totalSeconds += p.seconds; totalSeconds += p.seconds;
p.totalMeters = totalMeters; p.totalMeters = totalMeters;
@ -313,7 +330,7 @@ function displayTrack(xml, update) {
var lineString = new ol.geom.LineString(points); var lineString = new ol.geom.LineString(points);
var lineFeature = new ol.Feature({ var lineFeature = new ol.Feature({
geometry: lineString, geometry: lineString
}); });
layerTrack.getSource().addFeature(lineFeature); layerTrack.getSource().addFeature(lineFeature);
@ -341,66 +358,82 @@ function displayTrack(xml, update) {
}); });
map.addControl(zoomToExtentControl); map.addControl(zoomToExtentControl);
updateSummary(p.timestamp, totalMeters, totalSeconds); ns.updateSummary(p.timestamp, totalMeters, totalSeconds);
if (p.tid != trackid) { if (p.tid !== ns.config.trackid) {
trackid = p.tid; ns.config.trackid = p.tid;
setTrack(trackid); ns.setTrack(ns.config.trackid);
} }
if (document.getElementById('bottom').style.display == 'block') { ns.updateChart();
// update altitudes chart
chart.clearChart();
displayChart();
} }
}
function clearMap() { /**
* Clear map
*/
function clearMap() {
if (layerTrack) { if (layerTrack) {
layerTrack.getSource().clear(); layerTrack.getSource().clear();
} }
if (layerMarkers) { if (layerMarkers) {
layerMarkers.getSource().clear(); layerMarkers.getSource().clear();
} }
} }
function setMarker(p, i, posLen) { /**
* Set marker
* @param {uLogger.Position} pos
* @param {number} id
* @param {number} posLen
*/
function setMarker(pos, id, posLen) {
// marker // marker
var marker = new ol.Feature({ var marker = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([p.longitude, p.latitude])) geometry: new ol.geom.Point(ol.proj.fromLonLat([pos.longitude, pos.latitude]))
}); });
if (latest == 1) { var iconStyle;
var iconStyle = olStyles['red']; if (ns.isLatest()) {
} else if (i == 0) { iconStyle = olStyles['red'];
var iconStyle = olStyles['green']; } else if (id === 0) {
} else if (i == posLen - 1) { iconStyle = olStyles['green'];
var iconStyle = olStyles['red']; } else if (id === posLen - 1) {
iconStyle = olStyles['red'];
} else { } else {
var iconStyle = olStyles['white']; iconStyle = olStyles['white'];
} }
marker.setStyle(iconStyle); marker.setStyle(iconStyle);
marker.setId(i); marker.setId(id);
marker.set('p', p); marker.set('p', pos);
marker.set('posLen', posLen); marker.set('posLen', posLen);
layerMarkers.getSource().addFeature(marker); layerMarkers.getSource().addFeature(marker);
} }
function addChartEvent(chart, data) { /**
* Add listener on chart to show position on map
* @param {google.visualization.LineChart} chart
* @param {google.visualization.DataTable} data
*/
function addChartEvent(chart, data) {
google.visualization.events.addListener(chart, 'select', function () { google.visualization.events.addListener(chart, 'select', function () {
var selection = chart.getSelection()[0]; var selection = chart.getSelection()[0];
if (selection) { if (selection) {
var id = data.getValue(selection.row, 0) - 1; var id = data.getValue(selection.row, 0) - 1;
var marker = layerMarkers.getSource().getFeatureById(id); var marker = layerMarkers.getSource().getFeatureById(id);
var url = marker.get('src');
var initStyle = marker.getStyle(); var initStyle = marker.getStyle();
var iconStyle = olStyles['gold']; var iconStyle = olStyles['gold'];
marker.setStyle(iconStyle); marker.setStyle(iconStyle);
altTimeout = setTimeout(function () { marker.setStyle(initStyle); }, 2000); setTimeout(function () {
marker.setStyle(initStyle);
}, 2000);
} }
}); });
} }
//20.597985430276808,52.15547181298076,21.363595171488573,52.33750879522563 /**
function getBounds() { * Get map bounds
* eg. (20.597985430276808, 52.15547181298076, 21.363595171488573, 52.33750879522563)
* @returns {number[]} Bounds
*/
function getBounds() {
var extent = map.getView().calculateExtent(map.getSize()); var extent = map.getView().calculateExtent(map.getSize());
var bounds = ol.proj.transformExtent(extent, 'EPSG:900913', 'EPSG:4326'); var bounds = ol.proj.transformExtent(extent, 'EPSG:900913', 'EPSG:4326');
var lon_sw = bounds[0]; var lon_sw = bounds[0];
@ -408,24 +441,54 @@ function getBounds() {
var lon_ne = bounds[2]; var lon_ne = bounds[2];
var lat_ne = bounds[3]; var lat_ne = bounds[3];
return [lon_sw, lat_sw, lon_ne, lat_ne]; return [lon_sw, lat_sw, lon_ne, lat_ne];
} }
function zoomToExtent() { /**
map.getView().fit(layerMarkers.getSource().getExtent()) * Zoom to track extent
} */
function zoomToExtent() {
map.getView().fit(layerMarkers.getSource().getExtent());
}
function zoomToBounds(b) { /**
var bounds = ol.proj.transformExtent(b, 'EPSG:4326', 'EPSG:900913'); * Zoom to bounds
map.getView().fit(bounds); * @param {number[]} bounds
} */
function zoomToBounds(bounds) {
var extent = ol.proj.transformExtent(bounds, 'EPSG:4326', 'EPSG:900913');
map.getView().fit(extent);
}
function updateSize() { /**
* Update size
*/
function updateSize() {
map.updateSize(); map.updateSize();
} }
function getExtentImg() { /**
* Get extent image
* @returns {HTMLImageElement}
*/
function getExtentImg() {
var extentImg = document.createElement('img'); var extentImg = document.createElement('img');
extentImg.src = 'images/extent.svg'; extentImg.src = 'images/extent.svg';
extentImg.style.width = '60%'; extentImg.style.width = '60%';
return extentImg; return extentImg;
} }
return {
name: name,
init: init,
cleanup: cleanup,
displayTrack: displayTrack,
clearMap: clearMap,
setMarker: setMarker,
addChartEvent: addChartEvent,
getBounds: getBounds,
zoomToExtent: zoomToExtent,
zoomToBounds: zoomToBounds,
updateSize: updateSize
}
})(uLogger);

1332
js/main.js

File diff suppressed because it is too large Load Diff

View File

@ -16,63 +16,62 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
function changePass() { /** @namespace */
var form = '<form id="passForm" method="post" onsubmit="submitPass(); return false">'; var uLogger = window.uLogger || {};
form += '<label><b>' + lang['oldpassword'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="oldpass" required>'; (function (ns) {
form += '<label><b>' + lang['newpassword'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass" required>';
form += '<label><b>' + lang['newpasswordrepeat'] + '</b></label><input type="password" placeholder="' + lang['passwordenter'] + '" name="pass2" required>';
form += '<button type="button" onclick="removeModal()">' + lang['cancel'] + '</button><button type="submit">' + lang['submit'] + '</button>';
form += '</form>';
showModal(form);
}
function submitPass() { /**
* Show change password dialog
*/
function changePass() {
var form = '<form id="passForm" method="post" onsubmit="uLogger.submitPass(); return false">';
form += '<label><b>' + ns.lang.strings['oldpassword'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="oldpass" required>';
form += '<label><b>' + ns.lang.strings['newpassword'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass" required>';
form += '<label><b>' + ns.lang.strings['newpasswordrepeat'] + '</b></label><input type="password" placeholder="' + ns.lang.strings['passwordenter'] + '" name="pass2" required>';
form += '<button type="button" onclick="uLogger.ui.removeModal()">' + ns.lang.strings['cancel'] + '</button><button type="submit">' + ns.lang.strings['submit'] + '</button>';
form += '</form>';
ns.ui.showModal(form);
}
/**
* Submit password form
*/
function submitPass() {
var form = document.getElementById('passForm'); var form = document.getElementById('passForm');
var oldpass = form.elements['oldpass'].value; var oldpass = form.elements['oldpass'].value;
var pass = form.elements['pass'].value; var pass = form.elements['pass'].value;
var pass2 = form.elements['pass2'].value; var pass2 = form.elements['pass2'].value;
if (!oldpass || !pass || !pass2) { if (!oldpass || !pass || !pass2) {
alert(lang['allrequired']); alert(ns.lang.strings['allrequired']);
return; return;
} }
if (pass != pass2) { if (pass !== pass2) {
alert(lang['passnotmatch']); alert(ns.lang.strings['passnotmatch']);
return; return;
} }
if (!pass_regex.test(pass)) { if (!ns.config.pass_regex.test(pass)) {
alert(lang['passlenmin'] + '\n' + lang['passrules']); alert(ns.lang.strings['passlenmin'] + '\n' + ns.lang.strings['passrules']);
return; return;
} }
var xhr = getXHR(); ns.post('utils/changepass.php',
xhr.onreadystatechange = function () { {
if (xhr.readyState == 4) { oldpass: oldpass,
var error = true; pass: pass
var message = ''; },
if (xhr.status == 200) { {
var xml = xhr.responseXML; success: function () {
if (xml) { ns.ui.removeModal();
var root = xml.getElementsByTagName('root'); alert(ns.lang.strings['actionsuccess']);
if (root.length && getNode(root[0], 'error') == 0) { },
removeModal(); fail: function (message) {
alert(lang['actionsuccess']); alert(ns.lang.strings['actionfailure'] + '\n' + message);
error = false;
} else if (root.length) {
errorMsg = getNode(root[0], 'message');
if (errorMsg) { message = errorMsg; }
} }
});
} }
}
if (error) { // exports
alert(lang['actionfailure'] + '\n' + message); ns.changePass = changePass;
} ns.submitPass = submitPass;
xhr = null;
} })(uLogger);
}
xhr.open('POST', 'utils/changepass.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
var params = 'oldpass=' + encodeURIComponent(oldpass) + '&pass=' + encodeURIComponent(pass);
params = params.replace(/%20/g, '+');
xhr.send(params);
return;
}

View File

@ -16,91 +16,94 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
function editTrack() { /** @namespace */
var userForm = document.getElementsByName('user')[0]; var uLogger = window.uLogger || {};
var trackUser = (userForm !== undefined) ? userForm.options[userForm.selectedIndex].text : auth; (function (ns) {
if (trackUser != auth && !admin) {
alert(lang['owntrackswarn']); /**
* Show edit track dialog
*/
function editTrack() {
var userForm = ns.ui.userSelect;
var trackUser = (userForm) ? userForm.options[userForm.selectedIndex].text : ns.config.auth;
if (trackUser !== ns.config.auth && !ns.config.admin) {
alert(ns.lang.strings['owntrackswarn']);
return; return;
} }
var trackForm = document.getElementsByName('track')[0]; var trackForm = ns.ui.trackSelect;
if (trackForm.selectedIndex < 0) { if (trackForm.selectedIndex < 0) {
return; return;
} }
var trackId = trackForm.options[trackForm.selectedIndex].value; var trackId = trackForm.options[trackForm.selectedIndex].value;
var trackName = trackForm.options[trackForm.selectedIndex].text; var trackName = trackForm.options[trackForm.selectedIndex].text;
var message = '<div style="float:left">' + sprintf(lang['editingtrack'], '<b>' + htmlEncode(trackName) + '</b>') + '</div>'; var message = '<div style="float:left">' + ns.sprintf(ns.lang.strings['editingtrack'], '<b>' + ns.htmlEncode(trackName) + '</b>') + '</div>';
message += '<div class="red-button"><b><a href="javascript:void(0);" onclick="submitTrack(\'delete\'); return false">' + lang['deltrack'] + '</a></b></div>'; message += '<div class="red-button"><b><a href="javascript:void(0);" onclick="uLogger.submitTrack(\'delete\'); return false">' + ns.lang.strings['deltrack'] + '</a></b></div>';
message += '<div style="clear: both; padding-bottom: 1em;"></div>'; message += '<div style="clear: both; padding-bottom: 1em;"></div>';
var form = '<form id="trackForm" method="post" onsubmit="submitTrack(\'update\'); return false">'; var form = '<form id="trackForm" method="post" onsubmit="uLogger.submitTrack(\'update\'); return false">';
form += '<input type="hidden" name="trackid" value="' + trackId + '">'; form += '<input type="hidden" name="trackid" value="' + trackId + '">';
form += '<label><b>' + lang['trackname'] + '</b></label><input type="text" placeholder="' + lang['trackname'] + '" name="trackname" value="' + htmlEncode(trackName) + '" required>'; form += '<label><b>' + ns.lang.strings['trackname'] + '</b></label><input type="text" placeholder="' + ns.lang.strings['trackname'] + '" name="trackname" value="' + ns.htmlEncode(trackName) + '" required>';
form += '<div class="buttons"><button type="button" onclick="removeModal()">' + lang['cancel'] + '</button><button type="submit">' + lang['submit'] + '</button></div>'; form += '<div class="buttons"><button type="button" onclick="uLogger.ui.removeModal()">' + ns.lang.strings['cancel'] + '</button><button type="submit">' + ns.lang.strings['submit'] + '</button></div>';
form += '</form>'; form += '</form>';
showModal(message + form); ns.ui.showModal(message + form);
} }
function confirmedDelete(name) { /**
return confirm(sprintf(lang['trackdelwarn'], '"' + name + '"')); * Show confirmation dialog
} * @param {string} name
* @returns {boolean} True if confirmed
*/
function confirmedDelete(name) {
return confirm(ns.sprintf(ns.lang.strings['trackdelwarn'], '"' + name + '"'));
}
function submitTrack(action) { /**
* Submit form dialog
* @param action
*/
function submitTrack(action) {
var form = document.getElementById('trackForm'); var form = document.getElementById('trackForm');
var trackId = parseInt(form.elements['trackid'].value); var trackId = parseInt(form.elements['trackid'].value);
var trackName = form.elements['trackname'].value.trim(); var trackName = form.elements['trackname'].value.trim();
if (isNaN(trackId)) { if (isNaN(trackId)) {
alert(lang['allrequired']); alert(ns.lang.strings['allrequired']);
return; return;
} }
if (action != 'delete') { if (action !== 'delete') {
if (!trackName) { if (!trackName) {
alert(lang['allrequired']); alert(ns.lang.strings['allrequired']);
return; return;
} }
} else if (!confirmedDelete(trackName)) {
return;
}
ns.post('utils/handletrack.php',
{
action: action,
trackid: trackId,
trackname: trackName
},
{
success: function () {
ns.ui.removeModal();
alert(ns.lang.strings['actionsuccess']);
var el = ns.ui.trackSelect;
if (action === 'delete') {
el.remove(el.selectedIndex);
ns.map.clearMap();
ns.selectTrack();
} else { } else {
if (!confirmedDelete(trackName)) { el.options[el.selectedIndex].innerHTML = ns.htmlEncode(trackName);
return;
} }
},
fail: function (message) {
alert(ns.lang.strings['actionfailure'] + '\n' + message);
} }
var xhr = getXHR(); });
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var error = true;
var message = '';
if (xhr.status == 200) {
var xml = xhr.responseXML;
if (xml) {
var root = xml.getElementsByTagName('root');
if (root.length && getNode(root[0], 'error') == 0) {
removeModal();
alert(lang['actionsuccess']);
var f = document.getElementsByName('track')[0];
if (action == 'delete') {
// select current track in tracks form
f.remove(f.selectedIndex);
clearMap();
selectTrack(f);
} else {
f.options[f.selectedIndex].innerHTML = htmlEncode(trackName);
} }
error = false;
} else if (root.length) { ns.editTrack = editTrack;
errorMsg = getNode(root[0], 'message'); ns.submitTrack = submitTrack;
if (errorMsg) { message = errorMsg; }
} })(uLogger);
}
}
if (error) {
alert(lang['actionfailure'] + '\n' + message);
}
xhr = null;
}
}
xhr.open('POST', 'utils/handletrack.php', true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
var params = 'action=' + action + '&trackid=' + trackId + '&trackname=' + encodeURIComponent(trackName);
params = params.replace(/%20/g, '+');
xhr.send(params);
return;
}

60
js/typedefs.js Normal file
View File

@ -0,0 +1,60 @@
/* μlogger
*
* Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net)
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
/**
* @typedef uLogger.config
* @memberOf uLogger
* @type {Object}
* @property {number} interval
* @property {string} units
* @property {string} mapapi
* @property {?string} gkey
* @property {Object.<string, string>} ol_layers
* @property {number} init_latitude
* @property {number} init_longitude
* @property {boolean} admin
* @property {?string} auth
* @property {RegExp} pass_regex
* @property {number} strokeWeight
* @property {string} strokeColor
* @property {number} strokeOpacity
*/
/**
* @typedef uLogger.lang
* @memberOf uLogger
* @type {Object}
* @property {Object.<string, string>} strings
*/
/**
* @typedef {Object} uLogger.mapAPI.api
* @memberOf uLogger
* @type {Object}
* @property {string} name
* @property {function} init
* @property {function} cleanup
* @property {function(HTMLCollection, boolean)} displayTrack
* @property {function} clearMap
* @property {function(uLogger.Position, number, number)} setMarker
* @property {function} addChartEvent
* @property {function} getBounds
* @property {function} zoomToExtent
* @property {function} zoomToBounds
* @property {function} updateSize
*/