/*
* μ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 .
*/
import { lang as $, auth, config } from './initializer.js';
import GoogleMapsApi from './mapapi/api_gmaps.js';
import OpenLayersApi from './mapapi/api_openlayers.js';
import PositionDialogModel from './positiondialogmodel.js';
import ViewModel from './viewmodel.js';
import uDialog from './dialog.js';
import uObserve from './observe.js';
import uUtils from './utils.js';
/**
* @typedef {Object} MapViewModel.api
* @interface
* @memberOf MapViewModel
* @type {Object}
* @property {function(MapViewModel)} init
* @property {function} cleanup
* @property {function(uTrack, boolean)} displayTrack
* @property {function} clearMap
* @property {function(number)} animateMarker
* @property {function} getBounds
* @property {function} zoomToExtent
* @property {function} zoomToBounds
* @property {function} updateSize
*/
/**
* @class MapViewModel
*/
export default class MapViewModel extends ViewModel {
/**
* @param {uState} state
*/
constructor(state) {
super({
/** @type {?number} */
markerOver: null,
/** @type {?number} */
markerSelect: null,
// click handler
onMenuToggle: null
});
this.model.onMenuToggle = () => this.onMapResize();
this.state = state;
/** @type HTMLElement */
this.mapElement = document.querySelector('#map-canvas');
this.savedBounds = null;
this.api = null;
}
/**
* @return {MapViewModel}
*/
init() {
this.bindAll();
this.setObservers();
return this;
}
/**
* Dynamic change of map api
* @param {string} apiName API name
*/
loadMapAPI(apiName) {
if (this.api) {
try {
this.savedBounds = this.api.getBounds();
} catch (e) {
this.savedBounds = null;
}
this.api.cleanup();
}
this.api = this.getApi(apiName);
this.api.init()
.then(() => this.onReady())
.catch((e) => {
let txt = $._('apifailure', apiName);
if (e && e.message) {
txt += ` (${e.message})`;
}
uUtils.error(e, txt);
config.mapApi = (apiName === 'gmaps') ? 'openlayers' : 'gmaps';
});
}
/**
* @param {string} apiName
* @return {OpenLayersApi|GoogleMapsApi}
*/
getApi(apiName) {
return apiName === 'gmaps' ? new GoogleMapsApi(this) : new OpenLayersApi(this);
}
onReady() {
if (this.savedBounds) {
this.api.zoomToBounds(this.savedBounds);
}
if (this.state.currentTrack) {
this.api.displayTrack(this.state.currentTrack, this.savedBounds === null);
}
}
setObservers() {
config.onChanged('mapApi', (mapApi) => {
this.loadMapAPI(mapApi);
});
this.state.onChanged('currentTrack', (track) => {
if (!this.api) {
return;
}
this.api.clearMap();
if (track) {
uObserve.observe(track, 'positions', () => {
this.api.displayTrack(track, false);
this.api.zoomToExtent();
});
this.api.displayTrack(track, true);
}
});
}
/**
* Get popup html
* @param {number} id Position index
* @returns {HTMLDivElement}
*/
getPopupElement(id) {
const pos = this.state.currentTrack.positions[id];
const count = this.state.currentTrack.length;
const user = this.state.currentTrack.user;
const isEditable = auth.user && (auth.isAdmin || auth.user === user);
let date = '–––';
let time = '–––';
if (pos.timestamp > 0) {
const dateTime = uUtils.getTimeString(new Date(pos.timestamp * 1000));
date = dateTime.date;
time = `${dateTime.time}${dateTime.zone}`;
}
let provider = '';
if (pos.provider === 'gps') {
provider = `
`;
} else if (pos.provider === 'network') {
provider = `
`;
}
let editLink = '';
if (isEditable) {
editLink = ``;
}
let stats = '';
if (!this.state.showLatest) {
stats =
`

${$.getLocaleDuration(pos.totalSeconds)}

${$.getLocaleSpeed(pos.totalSpeed, true)}

${$.getLocaleDistanceMajor(pos.totalMeters, true)}
`;
}
const html =
`
${(pos.hasComment()) ? `` : ''}
${(pos.hasImage()) ? `
` : ''}

${date}

${time}
${(pos.speed !== null) ? `

${$.getLocaleSpeed(pos.speed, true)}
` : ''}
${(pos.altitude !== null) ? `

${$.getLocaleAltitude(pos.altitude, true)}
` : ''}
${(pos.accuracy !== null) ? `

${$.getLocaleAccuracy(pos.accuracy, true)}${provider}
` : ''}
${stats}
`;
const node = document.createElement('div');
node.setAttribute('id', 'popup');
node.innerHTML = html;
if (pos.hasImage()) {
const image = node.querySelector('#pimage img');
image.onclick = () => {
const modal = new uDialog(`