diff --git a/css/main.css b/css/main.css
index a006cc7..19591b1 100644
--- a/css/main.css
+++ b/css/main.css
@@ -209,6 +209,10 @@ label[for=user] {
max-width: 100%;
max-height: 300px;
border-radius: 10px;
+ cursor: pointer;
+}
+#pimage img:hover {
+ opacity: 0.7;
}
#pleft, #pright {
display: inline-block;
@@ -316,6 +320,27 @@ label[for=user] {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
+#modal.image {
+ padding-top: 0;
+ background-color: rgba(45, 45, 45, 0.95);
+ overflow: hidden;
+}
+
+#modal.image #modal-body img {
+ max-width: 100%;
+ max-height: 87vh;
+ height: auto;
+}
+
+#modal.image #modal-body {
+ width: 90%;
+ background-color: rgb(45, 45, 45);
+ text-align: center;
+}
+
+#modal.image #modal-header {
+ width: 90%;
+}
button {
color: white;
diff --git a/js/src/mapapi/api_gmaps.js b/js/src/mapapi/api_gmaps.js
index 6bc7b11..9dd794c 100644
--- a/js/src/mapapi/api_gmaps.js
+++ b/js/src/mapapi/api_gmaps.js
@@ -262,7 +262,7 @@ export default class GoogleMapsApi {
* @param {google.maps.Marker} marker
*/
popupOpen(id, marker) {
- this.popup.setContent(this.viewModel.getPopupHtml(id));
+ this.popup.setContent(this.viewModel.getPopupElement(id));
this.popup.open(this.map, marker);
this.viewModel.model.markerSelect = id;
}
diff --git a/js/src/mapapi/api_openlayers.js b/js/src/mapapi/api_openlayers.js
index 9ae353d..976c21f 100644
--- a/js/src/mapapi/api_openlayers.js
+++ b/js/src/mapapi/api_openlayers.js
@@ -299,7 +299,8 @@ export default class OpenLayersApi {
* @param {Coordinate} coordinate
*/
popupOpen(id, coordinate) {
- this.popup.getElement().firstElementChild.innerHTML = this.viewModel.getPopupHtml(id);
+ this.popup.getElement().firstElementChild.innerHTML = '';
+ this.popup.getElement().firstElementChild.appendChild(this.viewModel.getPopupElement(id));
this.popup.setPosition(coordinate);
this.viewModel.model.markerSelect = id;
}
@@ -310,6 +311,7 @@ export default class OpenLayersApi {
popupClose() {
// eslint-disable-next-line no-undefined
this.popup.setPosition(undefined);
+ this.popup.getElement().firstElementChild.innerHTML = '';
this.viewModel.model.markerSelect = null;
}
diff --git a/js/src/mapviewmodel.js b/js/src/mapviewmodel.js
index 1f293e6..5a9ab71 100644
--- a/js/src/mapviewmodel.js
+++ b/js/src/mapviewmodel.js
@@ -21,6 +21,7 @@ import { lang as $, config } from './initializer.js';
import GoogleMapsApi from './mapapi/api_gmaps.js';
import OpenLayersApi from './mapapi/api_openlayers.js';
import ViewModel from './viewmodel.js';
+import uDialog from './dialog.js';
import uObserve from './observe.js';
import uUtils from './utils.js';
@@ -138,9 +139,9 @@ export default class MapViewModel extends ViewModel {
/**
* Get popup html
* @param {number} id Position ID
- * @returns {string}
+ * @returns {HTMLDivElement}
*/
- getPopupHtml(id) {
+ getPopupElement(id) {
const pos = this.state.currentTrack.positions[id];
const count = this.state.currentTrack.length;
let date = '–––';
@@ -152,22 +153,22 @@ export default class MapViewModel extends ViewModel {
}
let provider = '';
if (pos.provider === 'gps') {
- provider = ` ()`;
+ provider = ` `;
} else if (pos.provider === 'network') {
- provider = ` ()`;
+ provider = ` `;
}
let stats = '';
if (!this.state.showLatest) {
stats =
`
-
+
${$.getLocaleDuration(pos.totalSeconds)}
${$.getLocaleSpeed(pos.totalSpeed, true)}
${$.getLocaleDistanceMajor(pos.totalMeters, true)}
`;
}
- return `
-
- `;
+ `;
+ 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(``);
+ const closeEl = modal.element.querySelector('#modal-close');
+ closeEl.onclick = () => modal.destroy();
+ modal.element.classList.add('image');
+ modal.show();
+ }
+ }
+ return node;
}
/**
diff --git a/js/test/api_gmaps.test.js b/js/test/api_gmaps.test.js
index ad5bade..09f4c43 100644
--- a/js/test/api_gmaps.test.js
+++ b/js/test/api_gmaps.test.js
@@ -374,15 +374,16 @@ describe('Google Maps map API tests', () => {
const id = 1;
spyOn(popup, 'setContent').and.callThrough();
spyOn(popup, 'open');
- mockViewModel.getPopupHtml = (i) => `content ${i}`;
- spyOn(mockViewModel, 'getPopupHtml').and.callThrough();
+ const popupEl = document.createElement('div');
+ mockViewModel.getPopupElement = () => popupEl;
+ spyOn(mockViewModel, 'getPopupElement').and.callThrough();
api.map = new google.maps.Map(container);
// when
api.popup = popup;
api.popupOpen(id, marker);
// then
- expect(mockViewModel.getPopupHtml).toHaveBeenCalledWith(id);
- expect(popup.setContent).toHaveBeenCalledWith(`content ${id}`);
+ expect(mockViewModel.getPopupElement).toHaveBeenCalledWith(id);
+ expect(popup.setContent).toHaveBeenCalledWith(popupEl);
expect(popup.open).toHaveBeenCalledWith(api.map, marker);
expect(api.viewModel.model.markerSelect).toBe(id);
});
diff --git a/js/test/api_openlayers.test.js b/js/test/api_openlayers.test.js
index 053c164..5d71929 100644
--- a/js/test/api_openlayers.test.js
+++ b/js/test/api_openlayers.test.js
@@ -521,9 +521,10 @@ describe('Openlayers map API tests', () => {
// given
const id = 1;
const coordinate = [ 1, 2 ];
- mockViewModel.getPopupHtml = (i) => `content ${i}`;
+ const popupEl = document.createElement('div');
+ mockViewModel.getPopupElement = () => popupEl;
mockViewModel.model.markerSelect = null;
- spyOn(mockViewModel, 'getPopupHtml').and.callThrough();
+ spyOn(mockViewModel, 'getPopupElement').and.callThrough();
api.map = mockMap;
const popupContainer = document.createElement('div');
const popupContent = document.createElement('div');
@@ -534,14 +535,15 @@ describe('Openlayers map API tests', () => {
api.popupOpen(id, coordinate);
// then
expect(api.popup.getPosition()).toEqual(coordinate);
- expect(api.popup.getElement().firstElementChild.innerHTML).toBe(`content ${id}`);
+ expect(mockViewModel.getPopupElement).toHaveBeenCalledWith(id);
+ expect(api.popup.getElement().firstElementChild.firstChild).toBe(popupEl);
expect(mockViewModel.model.markerSelect).toBe(id);
// when
api.popupClose();
// then
// eslint-disable-next-line no-undefined
expect(api.popup.getPosition()).toBe(undefined);
- expect(api.popup.getElement().firstElementChild.innerHTML).toBe('content 1');
+ expect(api.popup.getElement().firstElementChild.innerHTML).toBe('');
expect(mockViewModel.model.markerSelect).toBe(null);
});
});
diff --git a/js/test/mapviewmodel.test.js b/js/test/mapviewmodel.test.js
index 6782af6..1474704 100644
--- a/js/test/mapviewmodel.test.js
+++ b/js/test/mapviewmodel.test.js
@@ -259,8 +259,7 @@ describe('MapViewModel tests', () => {
const id = 0;
state.currentTrack = TrackFactory.getTrack(2);
// when
- const html = vm.getPopupHtml(id);
- const element = uUtils.nodeFromHtml(html);
+ const element = vm.getPopupElement(id);
// then
expect(element).toBeInstanceOf(HTMLDivElement);
expect(element.id).toBe('popup');
@@ -275,9 +274,9 @@ describe('MapViewModel tests', () => {
state.currentTrack = TrackFactory.getTrack(2);
state.showLatest = false;
// when
- const html = vm.getPopupHtml(id);
+ const popupEl = vm.getPopupElement(id);
// then
- expect(html).toContain('id="pright"');
+ expect(popupEl.querySelector('#pright')).toBeInstanceOf(HTMLDivElement);
});
it('should get popup without stats when track contains only latest positions', () => {
@@ -287,9 +286,9 @@ describe('MapViewModel tests', () => {
state.currentTrack = TrackFactory.getTrack(2);
state.showLatest = true;
// when
- const html = vm.getPopupHtml(id);
+ const popupEl = vm.getPopupElement(id);
// then
- expect(html).not.toContain('id="pright"');
+ expect(popupEl.querySelector('#pright')).toBeInstanceOf(HTMLDivElement);
});
it('should get marker svg source with given size and without extra border', () => {