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

View File

@ -17,192 +17,280 @@
*/ */
// 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 = [];
mapOptions = { /** @type {google.maps.InfoWindow} */
center: new google.maps.LatLng(init_latitude, init_longitude), var popup;
zoom: 8, /** @type {google.maps.PolylineOptions} */
mapTypeId: google.maps.MapTypeId.ROADMAP, var polyOptions;
scaleControl: true /** @type {google.maps.MapOptions} */
}; var mapOptions;
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); /** @type {number} */
} var timeoutHandle;
function cleanup() { var name = 'gmaps';
map = undefined; var isLoaded = false;
polies = undefined; var authError = false;
markers = undefined;
popups = undefined;
popup = undefined;
polyOptions = undefined;
mapOptions = undefined;
document.getElementById('map-canvas').innerHTML = '';
}
function displayTrack(xml, update) { /**
altitudes = {}; * Initialize map
var totalMeters = 0; */
var totalSeconds = 0; function init() {
// init polyline var url = '//maps.googleapis.com/maps/api/js?' + ((ns.config.gkey != null) ? ('key=' + ns.config.gkey + '&') : '') + 'callback=uLogger.mapAPI.gmaps.setLoaded';
var poly = new google.maps.Polyline(polyOptions); ns.addScript(url, 'mapapi_gmaps');
poly.setMap(map); if (!isLoaded) {
var path = poly.getPath(); throw new Error("Google Maps API not ready");
var latlngbounds = new google.maps.LatLngBounds();
var positions = xml.getElementsByTagName('position');
var posLen = positions.length;
for (var i = 0; i < posLen; i++) {
var p = parsePosition(positions[i], i);
totalMeters += p.distance;
totalSeconds += p.seconds;
p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds;
p.coordinates = new google.maps.LatLng(p.latitude, p.longitude);
// set marker
setMarker(p, i, posLen);
// update polyline
path.push(p.coordinates);
latlngbounds.extend(p.coordinates);
}
if (update) {
map.fitBounds(latlngbounds);
if (i == 1) {
// only one point, zoom out
zListener =
google.maps.event.addListenerOnce(map, 'bounds_changed', function (event) {
if (this.getZoom()) {
this.setZoom(15);
}
});
setTimeout(function () { google.maps.event.removeListener(zListener) }, 2000);
} }
start();
} }
polies.push(poly);
updateSummary(p.timestamp, totalMeters, totalSeconds); /**
if (p.tid != trackid) { * Start map engine when loaded
trackid = p.tid; */
setTrack(trackid); function start() {
} if (authError) {
if (document.getElementById('bottom').style.display == 'block') { gm_authFailure();
// update altitudes chart return;
chart.clearChart();
displayChart();
}
}
function clearMap() {
if (polies) {
for (var i = 0; i < polies.length; i++) {
polies[i].setMap(null);
} }
google.maps.visualRefresh = true;
// noinspection JSValidateTypes
polyOptions = {
strokeColor: ns.config.strokeColor,
strokeOpacity: ns.config.strokeOpacity,
strokeWeight: ns.config.strokeWeight
};
// noinspection JSValidateTypes
mapOptions = {
center: new google.maps.LatLng(ns.config.init_latitude, ns.config.init_longitude),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: true
};
map = new google.maps.Map(ns.ui.map, mapOptions);
} }
if (markers) {
for (var i = 0; i < markers.length; i++) {
google.maps.event.removeListener(popups[i].listener);
popups[i].setMap(null);
markers[i].setMap(null);
}
}
markers.length = 0;
polies.length = 0;
popups.lentgth = 0;
}
function setMarker(p, i, posLen) { /**
// marker * Clean up API
var marker = new google.maps.Marker({ */
map: map, function cleanup() {
position: new google.maps.LatLng(p.latitude, p.longitude), polies = [];
title: (new Date(p.timestamp * 1000)).toLocaleString() markers = [];
}); popups = [];
if (latest == 1) { marker.setIcon('images/marker-red.png') } map = null;
else if (i == 0) { marker.setIcon('images/marker-green.png') } polyOptions = null;
else if (i == posLen - 1) { marker.setIcon('images/marker-red.png') } mapOptions = null;
else { marker.setIcon('images/marker-white.png') } popup = null;
// popup ns.clearMapCanvas();
var content = getPopupHtml(p, i, posLen); }
popup = new google.maps.InfoWindow();
popup.listener = google.maps.event.addListener(marker, 'click', (function (marker, content) { /**
return function () { * Display track
popup.setContent(content); * @param {HTMLCollection} positions XML element
popup.open(map, marker); * @param {boolean} update Should fit bounds if true
if (document.getElementById('bottom').style.display == 'block') { */
var index = 0; function displayTrack(positions, update) {
for (var key in altitudes) { var totalMeters = 0;
if (altitudes.hasOwnProperty(key) && key == i) { var totalSeconds = 0;
chart.setSelection([{ row: index, column: null }]); // init polyline
break; var poly = new google.maps.Polyline(polyOptions);
} poly.setMap(map);
index++; var path = poly.getPath();
} var latlngbounds = new google.maps.LatLngBounds();
var posLen = positions.length;
for (var i = 0; i < posLen; i++) {
var p = ns.parsePosition(positions[i], i);
totalMeters += p.distance;
totalSeconds += p.seconds;
p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds;
// set marker
setMarker(p, i, posLen);
// update polyline
var coordinates = new google.maps.LatLng(p.latitude, p.longitude);
path.push(coordinates);
latlngbounds.extend(coordinates);
}
if (update) {
map.fitBounds(latlngbounds);
if (i === 1) {
// only one point, zoom out
var zListener =
google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
if (this.getZoom()) {
this.setZoom(15);
}
});
setTimeout(function () { google.maps.event.removeListener(zListener) }, 2000);
} }
} }
})(marker, content)); polies.push(poly);
markers.push(marker);
popups.push(popup);
}
function addChartEvent(chart, data) { ns.updateSummary(p.timestamp, totalMeters, totalSeconds);
google.visualization.events.addListener(chart, 'select', function () { if (p.tid !== ns.config.trackid) {
if (popup) { popup.close(); clearTimeout(altTimeout); } ns.config.trackid = p.tid;
var selection = chart.getSelection()[0]; ns.setTrack(ns.config.trackid);
if (selection) {
var id = data.getValue(selection.row, 0) - 1;
var icon = markers[id].getIcon();
markers[id].setIcon('images/marker-gold.png');
altTimeout = setTimeout(function () { markers[id].setIcon(icon); }, 2000);
} }
}); ns.updateChart();
}
//((52.20105108685229, 20.789387865580238), (52.292069558807135, 21.172192736185707))
function getBounds() {
var bounds = map.getBounds();
var lat_sw = bounds.getSouthWest().lat();
var lon_sw = bounds.getSouthWest().lng();
var lat_ne = bounds.getNorthEast().lat();
var lon_ne = bounds.getNorthEast().lng();
return [lon_sw, lat_sw, lon_ne, lat_ne];
}
function zoomToExtent() {
var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; i++) {
var coordinates = new google.maps.LatLng(markers[i].position.lat(), markers[i].position.lng());
latlngbounds.extend(coordinates);
} }
map.fitBounds(latlngbounds);
}
function zoomToBounds(b) { /**
var sw = new google.maps.LatLng(b[1], b[0]); * Clear map
var ne = new google.maps.LatLng(b[3], b[2]); */
var bounds = new google.maps.LatLngBounds(sw, ne); function clearMap() {
map.fitBounds(bounds); if (polies) {
} for (var i = 0; i < polies.length; i++) {
polies[i].setMap(null);
}
}
if (markers) {
for (var j = 0; j < markers.length; j++) {
google.maps.event.removeListener(popups[j].listener);
popups[j].setMap(null);
markers[j].setMap(null);
}
}
markers.length = 0;
polies.length = 0;
popups.lentgth = 0;
}
/**
* Set marker
* @param {uLogger.Position} pos
* @param {number} id
* @param {number} posLen
*/
function setMarker(pos, id, posLen) {
// marker
// noinspection JSCheckFunctionSignatures
var marker = new google.maps.Marker({
position: new google.maps.LatLng(pos.latitude, pos.longitude),
title: (new Date(pos.timestamp * 1000)).toLocaleString(),
map: map
});
if (ns.isLatest()) {
marker.setIcon('images/marker-red.png');
} else if (id === 0) {
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
var content = ns.getPopupHtml(pos, id, posLen);
popup = new google.maps.InfoWindow();
// noinspection JSUndefinedPropertyAssignment
popup.listener = google.maps.event.addListener(marker, 'click', (function (_marker, _content) {
return function () {
popup.setContent(_content);
popup.open(map, _marker);
ns.chartShowPosition(id);
}
})(marker, content));
markers.push(marker);
popups.push(popup);
}
/**
* 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 () {
if (popup) { popup.close(); clearTimeout(timeoutHandle); }
var selection = chart.getSelection()[0];
if (selection) {
var id = data.getValue(selection.row, 0) - 1;
var icon = markers[id].getIcon();
markers[id].setIcon('images/marker-gold.png');
timeoutHandle = setTimeout(function () { markers[id].setIcon(icon); }, 2000);
}
});
}
/**
* Get map bounds
* eg. ((52.20105108685229, 20.789387865580238), (52.292069558807135, 21.172192736185707))
* @returns {number[]} Bounds
*/
function getBounds() {
var bounds = map.getBounds();
var lat_sw = bounds.getSouthWest().lat();
var lon_sw = bounds.getSouthWest().lng();
var lat_ne = bounds.getNorthEast().lat();
var lon_ne = bounds.getNorthEast().lng();
return [lon_sw, lat_sw, lon_ne, lat_ne];
}
/**
* Zoom to track extent
*/
function zoomToExtent() {
var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; i++) {
var coordinates = new google.maps.LatLng(markers[i].position.lat(), markers[i].position.lng());
latlngbounds.extend(coordinates);
}
map.fitBounds(latlngbounds);
}
/**
* Zoom to bounds
* @param {number[]} 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,416 +16,479 @@
* 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';
var controls = [ /**
new ol.control.Zoom(), * Initialize map
new ol.control.Rotate(), */
new ol.control.ScaleLine(), function init() {
new ol.control.ZoomToExtent({ label: getExtentImg() }), 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);
}
var view = new ol.View({ ns.addCss('css/ol.css', 'ol_css');
center: ol.proj.fromLonLat([init_longitude, init_latitude]),
zoom: 8
});
map = new ol.Map({ var controls = [
target: 'map-canvas', new ol.control.Zoom(),
controls: controls, new ol.control.Rotate(),
view: view new ol.control.ScaleLine(),
}); new ol.control.ZoomToExtent({label: getExtentImg()})
];
// default layer: OpenStreetMap var view = new ol.View({
var osm = new ol.layer.Tile({ center: ol.proj.fromLonLat([ns.config.init_longitude, ns.config.init_latitude]),
name: 'OpenStreetMap', zoom: 8
visible: true, });
source: new ol.source.OSM()
});
map.addLayer(osm);
selectedLayer = osm;
// add extra layers map = new ol.Map({
for (var layerName in ol_layers) { target: 'map-canvas',
if (ol_layers.hasOwnProperty(layerName)) { controls: controls,
var layerUrl = ol_layers[layerName]; view: view
var ol_layer = new ol.layer.Tile({ });
name: layerName,
visible: false, // default layer: OpenStreetMap
source: new ol.source.XYZ({ var osm = new ol.layer.Tile({
url: layerUrl name: 'OpenStreetMap',
visible: true,
source: new ol.source.OSM()
});
map.addLayer(osm);
selectedLayer = osm;
// add extra layers
for (var layerName in ns.config.ol_layers) {
if (ns.config.ol_layers.hasOwnProperty(layerName)) {
var layerUrl = ns.config.ol_layers[layerName];
var ol_layer = new ol.layer.Tile({
name: layerName,
visible: false,
source: new ol.source.XYZ({
url: layerUrl
})
});
map.addLayer(ol_layer);
}
}
// init layers
var lineStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: ns.hexToRGBA(ns.config.strokeColor, ns.config.strokeOpacity),
width: ns.config.strokeWeight
}) })
}); });
map.addLayer(ol_layer); layerTrack = new ol.layer.Vector({
} name: 'Track',
} type: 'data',
source: new ol.source.Vector(),
style: lineStyle
});
layerMarkers = new ol.layer.Vector({
name: 'Markers',
type: 'data',
source: new ol.source.Vector()
});
map.addLayer(layerTrack);
map.addLayer(layerMarkers);
// init layers // styles
var lineStyle = new ol.style.Style({ olStyles = {};
stroke: new ol.style.Stroke({ var iconRed = new ol.style.Icon({
color: hexToRGBA(strokeColor, strokeOpacity), anchor: [0.5, 1],
width: strokeWeight src: 'images/marker-red.png'
}) });
}); var iconGreen = new ol.style.Icon({
layerTrack = new ol.layer.Vector({ anchor: [0.5, 1],
name: 'Track', src: 'images/marker-green.png'
type: 'data', });
source: new ol.source.Vector(), var iconWhite = new ol.style.Icon({
style: lineStyle anchor: [0.5, 1],
}); opacity: 0.7,
layerMarkers = new ol.layer.Vector({ src: 'images/marker-white.png'
name: 'Markers', });
type: 'data', var iconGold = new ol.style.Icon({
source: new ol.source.Vector() anchor: [0.5, 1],
}); src: 'images/marker-gold.png'
map.addLayer(layerTrack); });
map.addLayer(layerMarkers); olStyles['red'] = new ol.style.Style({
image: iconRed
});
olStyles['green'] = new ol.style.Style({
image: iconGreen
});
olStyles['white'] = new ol.style.Style({
image: iconWhite
});
olStyles['gold'] = new ol.style.Style({
image: iconGold
});
// styles // popups
olStyles = {}; var popupContainer = document.createElement('div');
var iconRed = new ol.style.Icon({ popupContainer.id = 'popup';
anchor: [ 0.5, 1 ], popupContainer.className = 'ol-popup';
src: 'images/marker-red.png' document.body.appendChild(popupContainer);
}); var popupCloser = document.createElement('a');
var iconGreen = new ol.style.Icon({ popupCloser.id = 'popup-closer';
anchor: [ 0.5, 1 ], popupCloser.className = 'ol-popup-closer';
src: 'images/marker-green.png' popupCloser.href = '#';
}); popupContainer.appendChild(popupCloser);
var iconWhite = new ol.style.Icon({ var popupContent = document.createElement('div');
anchor: [ 0.5, 1 ], popupContent.id = 'popup-content';
opacity: 0.7, popupContainer.appendChild(popupContent);
src: 'images/marker-white.png'
});
var iconGold = new ol.style.Icon({
anchor: [ 0.5, 1 ],
src: 'images/marker-gold.png'
});
olStyles['red'] = new ol.style.Style({
image: iconRed
});
olStyles['green'] = new ol.style.Style({
image: iconGreen
});
olStyles['white'] = new ol.style.Style({
image: iconWhite
});
olStyles['gold'] = new ol.style.Style({
image: iconGold
});
// popups var popup = new ol.Overlay({
var popupContainer = document.createElement('div'); element: popupContainer,
popupContainer.id = 'popup'; autoPan: true,
popupContainer.className = 'ol-popup'; autoPanAnimation: {
document.getElementsByTagName('body')[0].appendChild(popupContainer); duration: 250
var popupCloser = document.createElement('a');
popupCloser.id = 'popup-closer';
popupCloser.className = 'ol-popup-closer';
popupCloser.href = '#';
popupContainer.appendChild(popupCloser);
var popupContent = document.createElement('div');
popupContent.id = 'popup-content';
popupContainer.appendChild(popupContent);
var popup = new ol.Overlay({
element: popupContainer,
autoPan: true,
autoPanAnimation: {
duration: 250
}
});
popupCloser.onclick = function() {
popup.setPosition(undefined);
popupCloser.blur();
return false;
};
// add click handler to map to show popup
map.on('click', function(e) {
var coordinate = e.coordinate;
var feature = map.forEachFeatureAtPixel(e.pixel,
function(feature, layer) {
if (layer.get('name') == 'Markers') {
return feature;
} }
}); });
if (feature) {
var p = feature.get('p');
var i = feature.getId();
var posLen = feature.get('posLen');
// popup show
popup.setPosition(coordinate);
popupContent.innerHTML = getPopupHtml(p, i, posLen);
map.addOverlay(popup);
if (document.getElementById('bottom').style.display == 'block') {
var index = 0;
for (var key in altitudes) {
if (altitudes.hasOwnProperty(key) && key == i) {
chart.setSelection([{ row: index, column: null }]);
break;
}
index++;
}
}
} else {
// popup destroy
popup.setPosition(undefined);
}
});
// change mouse cursor when over marker popupCloser.onclick = function () {
map.on('pointermove', function(e) { // eslint-disable-next-line no-undefined
var hit = map.forEachFeatureAtPixel(e.pixel, function(feature, layer) { popup.setPosition(undefined);
if (layer.get('name') == 'Markers') { popupCloser.blur();
return true;
} else {
return false; return false;
} };
});
if (hit) {
this.getTargetElement().style.cursor = 'pointer';
} else {
this.getTargetElement().style.cursor = '';
}
});
// layer switcher // add click handler to map to show popup
var switcher = document.createElement('div'); map.on('click', function (e) {
switcher.id = 'switcher'; var coordinate = e.coordinate;
switcher.className = 'ol-control'; var feature = map.forEachFeatureAtPixel(e.pixel,
document.getElementsByTagName('body')[0].appendChild(switcher); function (_feature, _layer) {
var switcherContent = document.createElement('div'); if (_layer.get('name') === 'Markers') {
switcherContent.id = 'switcher-content'; return _feature;
switcherContent.className = 'ol-layerswitcher'; }
switcher.appendChild(switcherContent); return null;
});
map.getLayers().forEach(function (layer) { if (feature) {
var layerLabel = document.createElement('label'); var pos = feature.get('p');
layerLabel.innerHTML = layer.get('name'); var id = feature.getId();
switcherContent.appendChild(layerLabel); var posLen = feature.get('posLen');
// popup show
var layerRadio = document.createElement('input'); popup.setPosition(coordinate);
if (layer.get('type') === 'data') { popupContent.innerHTML = ns.getPopupHtml(pos, id, posLen);
layerRadio.type = 'checkbox'; map.addOverlay(popup);
layerLabel.className = 'ol-datalayer'; ns.chartShowPosition(id);
} else {
layerRadio.type = 'radio';
}
layerRadio.name = 'layer';
layerRadio.value = layer.get('name');
layerRadio.onclick = switchLayer;
if (layer.getVisible()) {
layerRadio.checked = true;
}
layerLabel.insertBefore(layerRadio, layerLabel.childNodes[0]);
});
function switchLayer() {
var layerName = this.value;
map.getLayers().forEach(function (layer) {
if (layer.get('name') === layerName) {
if (layer.get('type') === 'data') {
if (layer.getVisible()) {
layer.setVisible(false);
} else {
layer.setVisible(true);
}
} else { } else {
selectedLayer.setVisible(false); // popup destroy
selectedLayer = layer; // eslint-disable-next-line no-undefined
layer.setVisible(true); popup.setPosition(undefined);
} }
return; });
// change mouse cursor when over marker
map.on('pointermove', function (e) {
var hit = map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
return layer.get('name') === 'Markers';
});
if (hit) {
this.getTargetElement().style.cursor = 'pointer';
} else {
this.getTargetElement().style.cursor = '';
}
});
// layer switcher
var switcher = document.createElement('div');
switcher.id = 'switcher';
switcher.className = 'ol-control';
document.body.appendChild(switcher);
var switcherContent = document.createElement('div');
switcherContent.id = 'switcher-content';
switcherContent.className = 'ol-layerswitcher';
switcher.appendChild(switcherContent);
map.getLayers().forEach(function (layer) {
var layerLabel = document.createElement('label');
layerLabel.innerHTML = layer.get('name');
switcherContent.appendChild(layerLabel);
var layerRadio = document.createElement('input');
if (layer.get('type') === 'data') {
layerRadio.type = 'checkbox';
layerLabel.className = 'ol-datalayer';
} else {
layerRadio.type = 'radio';
}
layerRadio.name = 'layer';
layerRadio.value = layer.get('name');
layerRadio.onclick = switchLayer;
if (layer.getVisible()) {
layerRadio.checked = true;
}
layerLabel.insertBefore(layerRadio, layerLabel.childNodes[0]);
});
function switchLayer() {
var targetName = this.value;
map.getLayers().forEach(function (layer) {
if (layer.get('name') === targetName) {
if (layer.get('type') === 'data') {
if (layer.getVisible()) {
layer.setVisible(false);
} else {
layer.setVisible(true);
}
} else {
selectedLayer.setVisible(false);
selectedLayer = layer;
layer.setVisible(true);
}
}
});
} }
});
};
var switcherButton = document.createElement('button'); var switcherButton = document.createElement('button');
var layerImg = document.createElement('img'); var layerImg = document.createElement('img');
layerImg.src = 'images/layers.svg'; layerImg.src = 'images/layers.svg';
layerImg.style.width = '60%'; layerImg.style.width = '60%';
switcherButton.appendChild(layerImg); switcherButton.appendChild(layerImg);
var switcherHandle = function() { // eslint-disable-next-line func-style
var el = document.getElementById('switcher'); var switcherHandle = function () {
if (el.style.display === 'block') { var el = document.getElementById('switcher');
el.style.display = 'none'; if (el.style.display === 'block') {
} else { el.style.display = 'none';
el.style.display = 'block'; } else {
el.style.display = 'block';
}
};
switcherButton.addEventListener('click', switcherHandle, false);
switcherButton.addEventListener('touchstart', switcherHandle, false);
var element = document.createElement('div');
element.className = 'ol-switcher-button ol-unselectable ol-control';
element.appendChild(switcherButton);
var switcherControl = new ol.control.Control({
element: element
});
map.addControl(switcherControl);
} }
};
switcherButton.addEventListener('click', switcherHandle, false); /**
switcherButton.addEventListener('touchstart', switcherHandle, false); * Clean up API
*/
var element = document.createElement('div'); function cleanup() {
element.className = 'ol-switcher-button ol-unselectable ol-control'; map = null;
element.appendChild(switcherButton); layerTrack = null;
layerMarkers = null;
var switcherControl = new ol.control.Control({ selectedLayer = null;
element: element olStyles = null;
}); ns.removeElementById('popup');
map.addControl(switcherControl); ns.removeElementById('switcher');
} ns.clearMapCanvas();
function cleanup() {
map = undefined;
layerTrack = undefined;
layerMarkers = undefined;
selectedLayer = undefined;
olStyles = undefined;
removeElementById('popup');
removeElementById('switcher');
document.getElementById('map-canvas').innerHTML = '';
}
function displayTrack(xml, update) {
altitudes = {};
var totalMeters = 0;
var totalSeconds = 0;
var points = [];
var positions = xml.getElementsByTagName('position');
var posLen = positions.length;
for (var i = 0; i < posLen; i++) {
var p = parsePosition(positions[i], i);
totalMeters += p.distance;
totalSeconds += p.seconds;
p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds;
// set marker
setMarker(p, i, posLen);
// update polyline
var point = ol.proj.fromLonLat([p.longitude, p.latitude]);
points.push(point);
}
var lineString = new ol.geom.LineString(points);
var lineFeature = new ol.Feature({
geometry: lineString,
});
layerTrack.getSource().addFeature(lineFeature);
var extent = layerTrack.getSource().getExtent();
map.getControls().forEach(function (el) {
if (el instanceof ol.control.ZoomToExtent) {
map.removeControl(el);
} }
});
if (update) { /**
map.getView().fit(extent); * Display track
var zoom = map.getView().getZoom(); * @param {HTMLCollection} positions XML element
if (zoom > 20) { * @param {boolean} update Should fit bounds if true
map.getView().setZoom(20); */
extent = map.getView().calculateExtent(map.getSize()); function displayTrack(positions, update) {
var totalMeters = 0;
var totalSeconds = 0;
var points = [];
var posLen = positions.length;
for (var i = 0; i < posLen; i++) {
var p = ns.parsePosition(positions[i], i);
totalMeters += p.distance;
totalSeconds += p.seconds;
p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds;
// set marker
setMarker(p, i, posLen);
// update polyline
var point = ol.proj.fromLonLat([p.longitude, p.latitude]);
points.push(point);
}
var lineString = new ol.geom.LineString(points);
var lineFeature = new ol.Feature({
geometry: lineString
});
layerTrack.getSource().addFeature(lineFeature);
var extent = layerTrack.getSource().getExtent();
map.getControls().forEach(function (el) {
if (el instanceof ol.control.ZoomToExtent) {
map.removeControl(el);
}
});
if (update) {
map.getView().fit(extent);
var zoom = map.getView().getZoom();
if (zoom > 20) {
map.getView().setZoom(20);
extent = map.getView().calculateExtent(map.getSize());
}
}
var zoomToExtentControl = new ol.control.ZoomToExtent({
extent: extent,
label: getExtentImg()
});
map.addControl(zoomToExtentControl);
ns.updateSummary(p.timestamp, totalMeters, totalSeconds);
if (p.tid !== ns.config.trackid) {
ns.config.trackid = p.tid;
ns.setTrack(ns.config.trackid);
}
ns.updateChart();
} }
}
var zoomToExtentControl = new ol.control.ZoomToExtent({ /**
extent: extent, * Clear map
label: getExtentImg() */
}); function clearMap() {
map.addControl(zoomToExtentControl); if (layerTrack) {
layerTrack.getSource().clear();
}
if (layerMarkers) {
layerMarkers.getSource().clear();
}
}
updateSummary(p.timestamp, totalMeters, totalSeconds); /**
if (p.tid != trackid) { * Set marker
trackid = p.tid; * @param {uLogger.Position} pos
setTrack(trackid); * @param {number} id
} * @param {number} posLen
if (document.getElementById('bottom').style.display == 'block') { */
// update altitudes chart function setMarker(pos, id, posLen) {
chart.clearChart(); // marker
displayChart(); var marker = new ol.Feature({
} geometry: new ol.geom.Point(ol.proj.fromLonLat([pos.longitude, pos.latitude]))
} });
function clearMap() { var iconStyle;
if (layerTrack) { if (ns.isLatest()) {
layerTrack.getSource().clear(); iconStyle = olStyles['red'];
} } else if (id === 0) {
if (layerMarkers) { iconStyle = olStyles['green'];
layerMarkers.getSource().clear(); } else if (id === posLen - 1) {
} iconStyle = olStyles['red'];
} } else {
iconStyle = olStyles['white'];
function setMarker(p, i, posLen) { }
// marker
var marker = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([p.longitude, p.latitude]))
});
if (latest == 1) {
var iconStyle = olStyles['red'];
} else if (i == 0) {
var iconStyle = olStyles['green'];
} else if (i == posLen - 1) {
var iconStyle = olStyles['red'];
} else {
var iconStyle = olStyles['white'];
}
marker.setStyle(iconStyle);
marker.setId(i);
marker.set('p', p);
marker.set('posLen', posLen);
layerMarkers.getSource().addFeature(marker);
}
function addChartEvent(chart, data) {
google.visualization.events.addListener(chart, 'select', function () {
var selection = chart.getSelection()[0];
if (selection) {
var id = data.getValue(selection.row, 0) - 1;
var marker = layerMarkers.getSource().getFeatureById(id);
var url = marker.get('src');
var initStyle = marker.getStyle();
var iconStyle = olStyles['gold'];
marker.setStyle(iconStyle); marker.setStyle(iconStyle);
altTimeout = setTimeout(function () { marker.setStyle(initStyle); }, 2000); marker.setId(id);
marker.set('p', pos);
marker.set('posLen', posLen);
layerMarkers.getSource().addFeature(marker);
} }
});
}
//20.597985430276808,52.15547181298076,21.363595171488573,52.33750879522563 /**
function getBounds() { * Add listener on chart to show position on map
var extent = map.getView().calculateExtent(map.getSize()); * @param {google.visualization.LineChart} chart
var bounds = ol.proj.transformExtent(extent, 'EPSG:900913', 'EPSG:4326'); * @param {google.visualization.DataTable} data
var lon_sw = bounds[0]; */
var lat_sw = bounds[1]; function addChartEvent(chart, data) {
var lon_ne = bounds[2]; google.visualization.events.addListener(chart, 'select', function () {
var lat_ne = bounds[3]; var selection = chart.getSelection()[0];
return [lon_sw, lat_sw, lon_ne, lat_ne]; if (selection) {
} var id = data.getValue(selection.row, 0) - 1;
var marker = layerMarkers.getSource().getFeatureById(id);
var initStyle = marker.getStyle();
var iconStyle = olStyles['gold'];
marker.setStyle(iconStyle);
setTimeout(function () {
marker.setStyle(initStyle);
}, 2000);
}
});
}
function zoomToExtent() { /**
map.getView().fit(layerMarkers.getSource().getExtent()) * 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 bounds = ol.proj.transformExtent(extent, 'EPSG:900913', 'EPSG:4326');
var lon_sw = bounds[0];
var lat_sw = bounds[1];
var lon_ne = bounds[2];
var lat_ne = bounds[3];
return [lon_sw, lat_sw, lon_ne, lat_ne];
}
function zoomToBounds(b) { /**
var bounds = ol.proj.transformExtent(b, 'EPSG:4326', 'EPSG:900913'); * Zoom to track extent
map.getView().fit(bounds); */
} function zoomToExtent() {
map.getView().fit(layerMarkers.getSource().getExtent());
}
function updateSize() { /**
map.updateSize(); * Zoom to bounds
} * @param {number[]} bounds
*/
function zoomToBounds(bounds) {
var extent = ol.proj.transformExtent(bounds, 'EPSG:4326', 'EPSG:900913');
map.getView().fit(extent);
}
function getExtentImg() { /**
var extentImg = document.createElement('img'); * Update size
extentImg.src = 'images/extent.svg'; */
extentImg.style.width = '60%'; function updateSize() {
return extentImg; map.updateSize();
} }
/**
* Get extent image
* @returns {HTMLImageElement}
*/
function getExtentImg() {
var extentImg = document.createElement('img');
extentImg.src = 'images/extent.svg';
extentImg.style.width = '60%';
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);

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

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