Add basic position editing
This commit is contained in:
parent
260d576fc2
commit
9417d75632
11
css/main.css
11
css/main.css
@ -289,6 +289,17 @@ label[for=user] {
|
|||||||
color: #f0f8ff;
|
color: #f0f8ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pfooter div:first-child {
|
||||||
|
width: 40%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pfooter div:last-child {
|
||||||
|
width: 40%;
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
#bottom {
|
#bottom {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
|
@ -71,7 +71,7 @@ require_once(ROOT_DIR . "/helpers/upload.php");
|
|||||||
FROM " . self::db()->table('positions') . " p
|
FROM " . self::db()->table('positions') . " p
|
||||||
LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id)
|
LEFT JOIN " . self::db()->table('users') . " u ON (p.user_id = u.id)
|
||||||
LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id)
|
LEFT JOIN " . self::db()->table('tracks') . " t ON (p.track_id = t.id)
|
||||||
WHERE id = ? LIMIT 1";
|
WHERE p.id = ? LIMIT 1";
|
||||||
$params = [ $positionId ];
|
$params = [ $positionId ];
|
||||||
try {
|
try {
|
||||||
$this->loadWithQuery($query, $params);
|
$this->loadWithQuery($query, $params);
|
||||||
@ -91,6 +91,15 @@ require_once(ROOT_DIR . "/helpers/upload.php");
|
|||||||
return uDb::getInstance();
|
return uDb::getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has image
|
||||||
|
*
|
||||||
|
* @return bool True if has image
|
||||||
|
*/
|
||||||
|
public function hasImage() {
|
||||||
|
return !empty($this->image);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add position
|
* Add position
|
||||||
*
|
*
|
||||||
@ -135,6 +144,70 @@ require_once(ROOT_DIR . "/helpers/upload.php");
|
|||||||
return $positionId;
|
return $positionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save position to database
|
||||||
|
*
|
||||||
|
* @return bool True if success, false otherwise
|
||||||
|
*/
|
||||||
|
public function update() {
|
||||||
|
$ret = false;
|
||||||
|
if ($this->isValid) {
|
||||||
|
try {
|
||||||
|
$query = "UPDATE " . self::db()->table('positions') . " SET
|
||||||
|
time = " . self::db()->from_unixtime('?') . ", user_id = ?, track_id = ?, latitude = ?, longitude = ?, altitude = ?,
|
||||||
|
speed = ?, bearing = ?, accuracy = ?, provider = ?, comment = ?, image = ? WHERE id = ?";
|
||||||
|
$stmt = self::db()->prepare($query);
|
||||||
|
$params = [
|
||||||
|
$this->timestamp,
|
||||||
|
$this->userId,
|
||||||
|
$this->trackId,
|
||||||
|
$this->latitude,
|
||||||
|
$this->longitude,
|
||||||
|
$this->altitude,
|
||||||
|
$this->speed,
|
||||||
|
$this->bearing,
|
||||||
|
$this->accuracy,
|
||||||
|
$this->provider,
|
||||||
|
$this->comment,
|
||||||
|
$this->image,
|
||||||
|
$this->id
|
||||||
|
];
|
||||||
|
$stmt->execute($params);
|
||||||
|
$ret = true;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// TODO: handle exception
|
||||||
|
syslog(LOG_ERR, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete positions
|
||||||
|
*
|
||||||
|
* @return bool True if success, false otherwise
|
||||||
|
*/
|
||||||
|
public function delete() {
|
||||||
|
$ret = false;
|
||||||
|
if ($this->isValid) {
|
||||||
|
try {
|
||||||
|
$query = "DELETE FROM " . self::db()->table('positions') . " WHERE id = ?";
|
||||||
|
$stmt = self::db()->prepare($query);
|
||||||
|
$stmt->execute([ $this->id ]);
|
||||||
|
if ($this->hasImage()) {
|
||||||
|
uUpload::delete($this->image);
|
||||||
|
}
|
||||||
|
$ret = true;
|
||||||
|
$this->id = NULL;
|
||||||
|
$this->isValid = false;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// TODO: handle exception
|
||||||
|
syslog(LOG_ERR, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all user's positions, optionally limit to given track
|
* Delete all user's positions, optionally limit to given track
|
||||||
*
|
*
|
||||||
|
@ -309,9 +309,11 @@ export default class OpenLayersApi {
|
|||||||
* Close popup
|
* Close popup
|
||||||
*/
|
*/
|
||||||
popupClose() {
|
popupClose() {
|
||||||
|
if (this.popup) {
|
||||||
// eslint-disable-next-line no-undefined
|
// eslint-disable-next-line no-undefined
|
||||||
this.popup.setPosition(undefined);
|
this.popup.setPosition(undefined);
|
||||||
this.popup.getElement().firstElementChild.innerHTML = '';
|
this.popup.getElement().firstElementChild.innerHTML = '';
|
||||||
|
}
|
||||||
this.viewModel.model.markerSelect = null;
|
this.viewModel.model.markerSelect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,6 +494,7 @@ export default class OpenLayersApi {
|
|||||||
* Clear map
|
* Clear map
|
||||||
*/
|
*/
|
||||||
clearMap() {
|
clearMap() {
|
||||||
|
this.popupClose();
|
||||||
if (this.layerTrack) {
|
if (this.layerTrack) {
|
||||||
this.layerTrack.getSource().clear();
|
this.layerTrack.getSource().clear();
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,10 @@
|
|||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { lang as $, config } from './initializer.js';
|
import { lang as $, auth, config } from './initializer.js';
|
||||||
import GoogleMapsApi from './mapapi/api_gmaps.js';
|
import GoogleMapsApi from './mapapi/api_gmaps.js';
|
||||||
import OpenLayersApi from './mapapi/api_openlayers.js';
|
import OpenLayersApi from './mapapi/api_openlayers.js';
|
||||||
|
import PositionDialogModel from './positiondialogmodel.js';
|
||||||
import ViewModel from './viewmodel.js';
|
import ViewModel from './viewmodel.js';
|
||||||
import uDialog from './dialog.js';
|
import uDialog from './dialog.js';
|
||||||
import uObserve from './observe.js';
|
import uObserve from './observe.js';
|
||||||
@ -138,12 +139,14 @@ export default class MapViewModel extends ViewModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get popup html
|
* Get popup html
|
||||||
* @param {number} id Position ID
|
* @param {number} id Position index
|
||||||
* @returns {HTMLDivElement}
|
* @returns {HTMLDivElement}
|
||||||
*/
|
*/
|
||||||
getPopupElement(id) {
|
getPopupElement(id) {
|
||||||
const pos = this.state.currentTrack.positions[id];
|
const pos = this.state.currentTrack.positions[id];
|
||||||
const count = this.state.currentTrack.length;
|
const count = this.state.currentTrack.length;
|
||||||
|
const user = this.state.currentTrack.user;
|
||||||
|
const isEditable = auth.user && (auth.isAdmin || auth.user === user);
|
||||||
let date = '–––';
|
let date = '–––';
|
||||||
let time = '–––';
|
let time = '–––';
|
||||||
if (pos.timestamp > 0) {
|
if (pos.timestamp > 0) {
|
||||||
@ -157,6 +160,10 @@ export default class MapViewModel extends ViewModel {
|
|||||||
} else if (pos.provider === 'network') {
|
} else if (pos.provider === 'network') {
|
||||||
provider = ` <img class="icon" alt="${$._('network')}" title="${$._('network')}" src="images/network_dark.svg">`;
|
provider = ` <img class="icon" alt="${$._('network')}" title="${$._('network')}" src="images/network_dark.svg">`;
|
||||||
}
|
}
|
||||||
|
let editLink = '';
|
||||||
|
if (isEditable) {
|
||||||
|
editLink = `<a id="editposition" class="menu-link" data-bind="onUserAdd">${$._('editposition')}</a>`;
|
||||||
|
}
|
||||||
let stats = '';
|
let stats = '';
|
||||||
if (!this.state.showLatest) {
|
if (!this.state.showLatest) {
|
||||||
stats =
|
stats =
|
||||||
@ -182,7 +189,7 @@ export default class MapViewModel extends ViewModel {
|
|||||||
${(pos.altitude !== null) ? `<img class="icon" alt="${$._('altitude')}" title="${$._('altitude')}" src="images/altitude_dark.svg">${$.getLocaleAltitude(pos.altitude, true)}<br>` : ''}
|
${(pos.altitude !== null) ? `<img class="icon" alt="${$._('altitude')}" title="${$._('altitude')}" src="images/altitude_dark.svg">${$.getLocaleAltitude(pos.altitude, true)}<br>` : ''}
|
||||||
${(pos.accuracy !== null) ? `<img class="icon" alt="${$._('accuracy')}" title="${$._('accuracy')}" src="images/accuracy_dark.svg">${$.getLocaleAccuracy(pos.accuracy, true)}${provider}<br>` : ''}
|
${(pos.accuracy !== null) ? `<img class="icon" alt="${$._('accuracy')}" title="${$._('accuracy')}" src="images/accuracy_dark.svg">${$.getLocaleAccuracy(pos.accuracy, true)}${provider}<br>` : ''}
|
||||||
</div>${stats}</div>
|
</div>${stats}</div>
|
||||||
<div id="pfooter">${$._('pointof', id + 1, count)}</div>`;
|
<div id="pfooter"><div>${$._('pointof', id + 1, count)}</div><div>${editLink}</div></div>`;
|
||||||
const node = document.createElement('div');
|
const node = document.createElement('div');
|
||||||
node.setAttribute('id', 'popup');
|
node.setAttribute('id', 'popup');
|
||||||
node.innerHTML = html;
|
node.innerHTML = html;
|
||||||
@ -196,6 +203,13 @@ export default class MapViewModel extends ViewModel {
|
|||||||
modal.show();
|
modal.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isEditable) {
|
||||||
|
const edit = node.querySelector('#editposition');
|
||||||
|
edit.onclick = () => {
|
||||||
|
const vm = new PositionDialogModel(this.state, id);
|
||||||
|
vm.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,17 @@ export default class uObserve {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger notify of property observers
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {string} property
|
||||||
|
*/
|
||||||
|
static forceUpdate(obj, property) {
|
||||||
|
const value = obj._values[property];
|
||||||
|
const observers = obj._observers[property];
|
||||||
|
this.notify(observers, value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if object property is observed;
|
* Check if object property is observed;
|
||||||
* Optionally check if it is observed by given observer
|
* Optionally check if it is observed by given observer
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import uAjax from './ajax.js';
|
||||||
import uUtils from './utils.js';
|
import uUtils from './utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,4 +92,60 @@ export default class uPosition {
|
|||||||
get totalSpeed() {
|
get totalSpeed() {
|
||||||
return this.totalSeconds ? this.totalMeters / this.totalSeconds : 0;
|
return this.totalSeconds ? this.totalMeters / this.totalSeconds : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<void, Error>}
|
||||||
|
*/
|
||||||
|
delete() {
|
||||||
|
return uPosition.update({
|
||||||
|
action: 'delete',
|
||||||
|
posid: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<void, Error>}
|
||||||
|
*/
|
||||||
|
save() {
|
||||||
|
return uPosition.update({
|
||||||
|
action: 'update',
|
||||||
|
posid: this.id,
|
||||||
|
comment: this.comment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save track data
|
||||||
|
* @param {Object} data
|
||||||
|
* @return {Promise<void, Error>}
|
||||||
|
*/
|
||||||
|
static update(data) {
|
||||||
|
return uAjax.post('utils/handleposition.php', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate distance to target point using haversine formula
|
||||||
|
* @param {uPosition} target
|
||||||
|
* @return {number} Distance in meters
|
||||||
|
*/
|
||||||
|
distanceTo(target) {
|
||||||
|
const lat1 = uUtils.deg2rad(this.latitude);
|
||||||
|
const lon1 = uUtils.deg2rad(this.longitude);
|
||||||
|
const lat2 = uUtils.deg2rad(target.latitude);
|
||||||
|
const lon2 = uUtils.deg2rad(target.longitude);
|
||||||
|
const latD = lat2 - lat1;
|
||||||
|
const lonD = lon2 - lon1;
|
||||||
|
const bearing = 2 * Math.asin(Math.sqrt((Math.sin(latD / 2) ** 2) + Math.cos(lat1) * Math.cos(lat2) * (Math.sin(lonD / 2) ** 2)));
|
||||||
|
return bearing * 6371000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate time elapsed since target point
|
||||||
|
* @param {uPosition} target
|
||||||
|
* @return {number} Number of seconds
|
||||||
|
*/
|
||||||
|
secondsTo(target) {
|
||||||
|
return this.timestamp - target.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
114
js/src/positiondialogmodel.js
Normal file
114
js/src/positiondialogmodel.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* μlogger
|
||||||
|
*
|
||||||
|
* Copyright(C) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { lang as $ } from './initializer.js';
|
||||||
|
import ViewModel from './viewmodel.js';
|
||||||
|
import uDialog from './dialog.js';
|
||||||
|
import uObserve from './observe.js';
|
||||||
|
import uUtils from './utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PositionDialogModel
|
||||||
|
*/
|
||||||
|
export default class PositionDialogModel extends ViewModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {uState} state
|
||||||
|
* @param {number} positionIndex
|
||||||
|
*/
|
||||||
|
constructor(state, positionIndex) {
|
||||||
|
super({
|
||||||
|
onPositionDelete: null,
|
||||||
|
onPositionUpdate: null,
|
||||||
|
onCancel: null,
|
||||||
|
comment: ''
|
||||||
|
});
|
||||||
|
this.state = state;
|
||||||
|
this.positionIndex = positionIndex;
|
||||||
|
this.position = this.state.currentTrack.positions[positionIndex];
|
||||||
|
this.model.onPositionDelete = () => this.onPositionDelete();
|
||||||
|
this.model.onPositionUpdate = () => this.onPositionUpdate();
|
||||||
|
this.model.onCancel = () => this.onCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const html = this.getHtml();
|
||||||
|
this.dialog = new uDialog(html);
|
||||||
|
this.dialog.show();
|
||||||
|
this.bindAll(this.dialog.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getHtml() {
|
||||||
|
return `<div style="float:left;">${$._('editingposition', this.positionIndex + 1, `<b>${uUtils.htmlEncode(this.position.trackname)}</b>`)}</div>
|
||||||
|
<div class="red-button button-resolve"><b><a data-bind="onPositionDelete">${$._('delposition')}</a></b></div>
|
||||||
|
<div style="clear: both; padding-bottom: 1em;"></div>
|
||||||
|
<form id="positionForm">
|
||||||
|
<label><b>${$._('comment')}</b></label><br>
|
||||||
|
<textarea style="width:100%;" maxlength="255" rows="5" placeholder="${$._('comment')}" name="comment" data-bind="comment">${uUtils.htmlEncode(this.position.comment)}</textarea>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button-reject" data-bind="onCancel" type="button">${$._('cancel')}</button>
|
||||||
|
<button class="button-resolve" data-bind="onPositionUpdate" type="submit">${$._('submit')}</button>
|
||||||
|
</div>
|
||||||
|
</form>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionDelete() {
|
||||||
|
if (uDialog.isConfirmed($._('positiondelwarn', this.positionIndex + 1, uUtils.htmlEncode(this.position.trackname)))) {
|
||||||
|
this.position.delete()
|
||||||
|
.then(() => {
|
||||||
|
const track = this.state.currentTrack;
|
||||||
|
this.state.currentTrack = null;
|
||||||
|
track.positions.splice(this.positionIndex, 1);
|
||||||
|
track.recalculatePositions();
|
||||||
|
this.state.currentTrack = track;
|
||||||
|
this.dialog.destroy();
|
||||||
|
}).catch((e) => { uUtils.error(e, `${$._('actionfailure')}\n${e.message}`); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionUpdate() {
|
||||||
|
if (this.validate()) {
|
||||||
|
this.position.comment = this.model.comment;
|
||||||
|
this.position.save()
|
||||||
|
.then(() => {
|
||||||
|
uObserve.forceUpdate(this.state, 'currentTrack');
|
||||||
|
this.dialog.destroy()
|
||||||
|
})
|
||||||
|
.catch((e) => { uUtils.error(e, `${$._('actionfailure')}\n${e.message}`); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
this.dialog.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate form
|
||||||
|
* @return {boolean} True if valid
|
||||||
|
*/
|
||||||
|
validate() {
|
||||||
|
if (this.model.comment === this.position.comment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,8 @@ export default class uTrack extends uPositionSet {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.plotData = [];
|
this.plotData = [];
|
||||||
this.maxId = 0;
|
this.maxId = 0;
|
||||||
|
this.totalMeters = 0;
|
||||||
|
this.totalSeconds = 0;
|
||||||
this.listItem(id, name);
|
this.listItem(id, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,8 +61,14 @@ export default class uTrack extends uPositionSet {
|
|||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
|
this.clearTrackCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTrackCounters() {
|
||||||
this.maxId = 0;
|
this.maxId = 0;
|
||||||
this.plotData.length = 0;
|
this.plotData.length = 0;
|
||||||
|
this.totalMeters = 0;
|
||||||
|
this.totalSeconds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,30 +92,16 @@ export default class uTrack extends uPositionSet {
|
|||||||
* @param {boolean=} isUpdate If true append to old data
|
* @param {boolean=} isUpdate If true append to old data
|
||||||
*/
|
*/
|
||||||
fromJson(posArr, isUpdate = false) {
|
fromJson(posArr, isUpdate = false) {
|
||||||
let totalMeters = 0;
|
|
||||||
let totalSeconds = 0;
|
|
||||||
let positions = [];
|
let positions = [];
|
||||||
if (isUpdate && this.hasPositions) {
|
if (isUpdate && this.hasPositions) {
|
||||||
positions = this.positions;
|
positions = this.positions;
|
||||||
const last = positions[this.length - 1];
|
|
||||||
totalMeters = last.totalMeters;
|
|
||||||
totalSeconds = last.totalSeconds;
|
|
||||||
} else {
|
} else {
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
for (const pos of posArr) {
|
for (const pos of posArr) {
|
||||||
const position = uPosition.fromJson(pos);
|
const position = uPosition.fromJson(pos);
|
||||||
totalMeters += position.meters;
|
this.calculatePosition(position);
|
||||||
totalSeconds += position.seconds;
|
|
||||||
position.totalMeters = totalMeters;
|
|
||||||
position.totalSeconds = totalSeconds;
|
|
||||||
positions.push(position);
|
positions.push(position);
|
||||||
if (position.altitude != null) {
|
|
||||||
this.plotData.push({ x: position.totalMeters, y: position.altitude });
|
|
||||||
}
|
|
||||||
if (position.id > this.maxId) {
|
|
||||||
this.maxId = position.id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// update at the end to avoid observers update invidual points
|
// update at the end to avoid observers update invidual points
|
||||||
this.positions = positions;
|
this.positions = positions;
|
||||||
@ -243,4 +237,31 @@ export default class uTrack extends uPositionSet {
|
|||||||
return uAjax.post('utils/handletrack.php', data);
|
return uAjax.post('utils/handletrack.php', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recalculatePositions() {
|
||||||
|
this.clearTrackCounters();
|
||||||
|
let previous = null;
|
||||||
|
for (const position of this.positions) {
|
||||||
|
position.meters = previous ? position.distanceTo(previous) : 0;
|
||||||
|
position.seconds = previous ? position.secondsTo(previous) : 0;
|
||||||
|
this.calculatePosition(position);
|
||||||
|
previous = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate position total counters and plot data
|
||||||
|
* @param {uPosition} position
|
||||||
|
*/
|
||||||
|
calculatePosition(position) {
|
||||||
|
this.totalMeters += position.meters;
|
||||||
|
this.totalSeconds += position.seconds;
|
||||||
|
position.totalMeters = this.totalMeters;
|
||||||
|
position.totalSeconds = this.totalSeconds;
|
||||||
|
if (position.altitude != null) {
|
||||||
|
this.plotData.push({ x: position.totalMeters, y: position.altitude });
|
||||||
|
}
|
||||||
|
if (position.id > this.maxId) {
|
||||||
|
this.maxId = position.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,4 +317,13 @@ export default class uUtils {
|
|||||||
console.error(details);
|
console.error(details);
|
||||||
alert(message);
|
alert(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Degrees to radians
|
||||||
|
* @param {number} degrees
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
static deg2rad(degrees) {
|
||||||
|
return degrees * Math.PI / 180;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,12 @@ export default class ViewModel {
|
|||||||
observers.forEach(/** @param {HTMLElement} element */ (element) => {
|
observers.forEach(/** @param {HTMLElement} element */ (element) => {
|
||||||
const name = element.dataset[dataProp];
|
const name = element.dataset[dataProp];
|
||||||
if (name === key) {
|
if (name === key) {
|
||||||
if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement) {
|
if (element instanceof HTMLInputElement ||
|
||||||
|
element instanceof HTMLSelectElement ||
|
||||||
|
element instanceof HTMLTextAreaElement) {
|
||||||
this.onChangeBind(element, key);
|
this.onChangeBind(element, key);
|
||||||
} else if (element instanceof HTMLAnchorElement || element instanceof HTMLButtonElement) {
|
} else if (element instanceof HTMLAnchorElement ||
|
||||||
|
element instanceof HTMLButtonElement) {
|
||||||
this.onClickBind(element, key);
|
this.onClickBind(element, key);
|
||||||
} else {
|
} else {
|
||||||
this.viewUpdateBind(element, key);
|
this.viewUpdateBind(element, key);
|
||||||
|
@ -111,6 +111,11 @@ $lang["editingtrack"] = "You are editing track %s"; // substitutes track name
|
|||||||
$lang["deltrack"] = "Remove track";
|
$lang["deltrack"] = "Remove track";
|
||||||
$lang["trackname"] = "Track name";
|
$lang["trackname"] = "Track name";
|
||||||
$lang["edittrack"] = "Edit track";
|
$lang["edittrack"] = "Edit track";
|
||||||
|
$lang["positiondelwarn"] = "Warning!\n\nYou are going to permanently delete position %d of track %s.\n\nAre you sure?"; // substitutes position index and track name
|
||||||
|
$lang["editingposition"] = "You are editing position #%d of track %s"; // substitutes position index and track name
|
||||||
|
$lang["delposition"] = "Remove position";
|
||||||
|
$lang["comment"] = "Comment";
|
||||||
|
$lang["editposition"] = "Edit position";
|
||||||
$lang["passlenmin"] = "Password must be at least %d characters"; // substitutes password minimum length
|
$lang["passlenmin"] = "Password must be at least %d characters"; // substitutes password minimum length
|
||||||
$lang["passrules_1"] = "It should contain at least one lower case letter, one upper case letter";
|
$lang["passrules_1"] = "It should contain at least one lower case letter, one upper case letter";
|
||||||
$lang["passrules_2"] = "It should contain at least one lower case letter, one upper case letter and one digit";
|
$lang["passrules_2"] = "It should contain at least one lower case letter, one upper case letter and one digit";
|
||||||
|
65
utils/handleposition.php
Normal file
65
utils/handleposition.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/* μlogger
|
||||||
|
*
|
||||||
|
* Copyright(C) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once(dirname(__DIR__) . "/helpers/auth.php");
|
||||||
|
require_once(ROOT_DIR . "/helpers/lang.php");
|
||||||
|
require_once(ROOT_DIR . "/helpers/track.php");
|
||||||
|
require_once(ROOT_DIR . "/helpers/utils.php");
|
||||||
|
require_once(ROOT_DIR . "/helpers/config.php");
|
||||||
|
|
||||||
|
$auth = new uAuth();
|
||||||
|
|
||||||
|
$action = uUtils::postString('action');
|
||||||
|
$positionId = uUtils::postInt('posid');
|
||||||
|
$comment = uUtils::postString('comment');
|
||||||
|
|
||||||
|
$lang = (new uLang(uConfig::$lang))->getStrings();
|
||||||
|
|
||||||
|
if (empty($action) || empty($positionId)) {
|
||||||
|
uUtils::exitWithError($lang["servererror"]);
|
||||||
|
}
|
||||||
|
$position = new uPosition($positionId);
|
||||||
|
if (!$position->isValid ||
|
||||||
|
(!$auth->isAuthenticated() || (!$auth->isAdmin() && $auth->user->id !== $position->userId))) {
|
||||||
|
uUtils::exitWithError($lang["servererror"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
|
||||||
|
case 'update':
|
||||||
|
$position->comment = $comment;
|
||||||
|
if ($position->update() === false) {
|
||||||
|
uUtils::exitWithError($lang["servererror"]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
if ($position->delete() === false) {
|
||||||
|
uUtils::exitWithError($lang["servererror"]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
uUtils::exitWithError($lang["servererror"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uUtils::exitWithSuccess();
|
||||||
|
|
||||||
|
?>
|
Loading…
x
Reference in New Issue
Block a user