1176 lines
42 KiB
JavaScript
1176 lines
42 KiB
JavaScript
//jQuery plugins
|
|
import autosize from 'autosize';
|
|
import mousewheel from 'jquery-mousewheel';
|
|
import waitforimages from 'jquery.waitforimages';
|
|
import lightbox from './lightbox.js';
|
|
|
|
//Simplebar
|
|
import SimpleBar from 'simplebar';
|
|
window.SimpleBar = SimpleBar;
|
|
import ResizeObserver from 'resize-observer-polyfill';
|
|
window.ResizeObserver = ResizeObserver;
|
|
|
|
//Leaflet
|
|
import 'leaflet';
|
|
import 'leaflet-geometryutil';
|
|
import 'leaflet.heightgraph';
|
|
import './leaflet.helpers';
|
|
|
|
export default class Project {
|
|
|
|
constructor(oSpot) {
|
|
this.spot = oSpot;
|
|
}
|
|
|
|
pageInit(asHash) {
|
|
this.spot.tmp('first_exec', true);
|
|
this.initPage(asHash);
|
|
}
|
|
|
|
onResize() {
|
|
this.spot.tmp('map_offset', -1 * (this.isFeedPanelOpen()?this.spot.tmp('$Feed').outerWidth(true):0) / $('body').outerWidth(true));
|
|
|
|
if(typeof this.spot.tmp('elev') != 'undefined' && this.spot.tmp('elev')._showState) {
|
|
this.spot.tmp('elev').resize({width:this.getElevWidth()});
|
|
}
|
|
}
|
|
|
|
onSamePageMove(asHash) {
|
|
if(this.spot.tmp('first_exec')) this.initPage(asHash);
|
|
else if(asHash.items[0] != this.spot.vars(['project', 'codename'])) {
|
|
this.spot.tmp('map').remove();
|
|
this.spot.tmp('$Map').empty();
|
|
this.spot.tmp('map', null);
|
|
this.spot.tmp('$PostList').empty();
|
|
this.setFeedUpdateTimer(-1);
|
|
this.initProject(asHash.items[0]);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
onQuitPage() {
|
|
this.setFeedUpdateTimer(-1);
|
|
return true;
|
|
}
|
|
|
|
onKeydown(oEvent) {
|
|
switch(oEvent.which) {
|
|
case 27:
|
|
if(!this.isLightboxOpen()) {
|
|
$bFeedPanelOpen = this.isFeedPanelOpen();
|
|
$bSettingsPanelOpen = this.isSettingsPanelOpen();
|
|
$bAnimation = ($bFeedPanelOpen && $bSettingsPanelOpen)?'none':null;
|
|
if($bFeedPanelOpen) this.toggleFeedPanel(false, $bAnimation);
|
|
if($bSettingsPanelOpen) this.toggleSettingsPanel(false, $bAnimation);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
initPage(asHash) {
|
|
if(asHash.items.length==0) this.spot.setHash(asHash.page, [this.spot.vars('default_project_codename')]);
|
|
else {
|
|
this.spot.tmp('$Projects', $('#projects'));
|
|
this.spot.tmp('$Map', $('#map'));
|
|
this.spot.tmp('$Poster', $('#poster'));
|
|
this.spot.tmp('$PostList', $('#posts_list'));
|
|
this.spot.tmp('$Feed', $('#feed'));
|
|
this.spot.tmp('$Settings', $('#settings'));
|
|
this.spot.tmp('$Title', $('#title'));
|
|
this.spot.tmp('updatable', true);
|
|
this.spot.tmp('out-of-data', false);
|
|
this.spot.tmp('markers', 'object');
|
|
this.spot.tmp('trail-markers', 'object');
|
|
this.spot.tmp('marker_size', {width: 32, height: 32});
|
|
|
|
if(!this.isMobile()) this.toggleFeedPanel(true, 'none');
|
|
|
|
//Lightbox options
|
|
lightbox.option({
|
|
alwaysShowNavOnTouchDevices: true,
|
|
albumLabel: '<i class="fa fa-fw fa-lg fa-media push"></i> %1 / %2',
|
|
fadeDuration: 300,
|
|
imageFadeDuration: 400,
|
|
positionFromTop: 0,
|
|
resizeDuration: 400,
|
|
hasVideo: true,
|
|
onMediaChange: (oMedia) => {
|
|
this.spot.updateHash('media', oMedia.id);
|
|
if(oMedia.set == 'post-medias') this.goToPost({type: 'media', id: oMedia.id});
|
|
},
|
|
onClosing: () => {this.spot.flushHash();}
|
|
});
|
|
|
|
//Assign Track Type Colors
|
|
this.spot.tmp('track-type-styles', 'object');
|
|
$('#legend').find('.line').each((iKey, oLegend) => {
|
|
var $Legend = $(oLegend);
|
|
var sTrackType = $Legend.attr('class').replace('line', '').trim();
|
|
this.spot.tmp(['track-type-styles', sTrackType], {weight: parseInt($Legend.css('height')), color: $Legend.css('background-color'), opacity: 1});
|
|
});
|
|
|
|
//Post Panel one-off init (see initPosts for project related init)
|
|
//Scrollbar
|
|
this.spot.tmp('simple-bar', new SimpleBar($('#feed-panel')[0]));
|
|
this.spot.tmp('simple-bar').getScrollElement().addEventListener('scroll', (oEvent) => {this.onFeedScroll(oEvent);});
|
|
|
|
//Add "Loading" Post
|
|
this.getPost({type: 'loading', headerless: true, formatted_time: '', relative_time: ''}).appendTo($('#loading'));
|
|
|
|
//Mobile Touchscreen Events
|
|
this.spot.tmp('$Feed').onSwipe(
|
|
(oPos) => {
|
|
this.$Panels = $('.leaflet-right')
|
|
.add(this.spot.tmp('$Feed'))
|
|
.each((iIndex, oElem) => {
|
|
let $Elem = $(oElem);
|
|
$Elem.data('initial-offset', parseInt($Elem.css('right')));
|
|
});
|
|
},
|
|
(oPos) => {
|
|
this.$Panels.each((iIndex, oElem) => {
|
|
let $Elem = $(oElem);
|
|
$Elem.css({'right': ($Elem.data('initial-offset') + Math.min(oPos.xStart - oPos.xMove, 0))+'px', 'transition': 'none'});
|
|
});
|
|
},
|
|
(oPos) => {
|
|
if(oPos.xEnd - oPos.xStart > 100 && Math.abs(oPos.yEnd - oPos.yStart) < 100) this.toggleFeedPanel(false);
|
|
this.$Panels.css({'right': '', 'transition': ''});
|
|
}
|
|
);
|
|
this.spot.tmp('$Settings').onSwipe(
|
|
(oPos) => {
|
|
this.$Panels = $('.leaflet-left')
|
|
.add(this.spot.tmp('$Settings'))
|
|
.each((iIndex, oElem) => {
|
|
let $Elem = $(oElem);
|
|
$Elem.data('initial-offset', parseInt($Elem.css('left')));
|
|
});
|
|
},
|
|
(oPos) => {
|
|
this.$Panels.each((iIndex, oElem) => {
|
|
let $Elem = $(oElem);
|
|
$Elem.css({'left': ($Elem.data('initial-offset') + Math.min(oPos.xMove - oPos.xStart, 0))+'px', 'transition': 'none'});
|
|
});
|
|
},
|
|
(oPos) => {
|
|
if(oPos.xEnd - oPos.xStart < -100 && Math.abs(oPos.yEnd - oPos.yStart) < 100) this.toggleSettingsPanel(false);
|
|
this.$Panels.css({'left': '', 'transition': ''});
|
|
}
|
|
);
|
|
|
|
//Feed Panel
|
|
this.initPosts();
|
|
|
|
//Settings Panel
|
|
this.initSettings();
|
|
|
|
//project Bootstrap
|
|
var oFocusPost = asHash.items[1]?{type: asHash.items[1], id: asHash.items[2]}:null;
|
|
this.initProject(asHash.items[0], oFocusPost);
|
|
}
|
|
}
|
|
|
|
toggleFeedPanel(bShow, sMapAction) {
|
|
let bOldValue = this.isFeedPanelOpen();
|
|
this.spot.tmp('$Projects').toggleClass('with-feed', (typeof bShow === 'undefined')?null:bShow);
|
|
let bNewValue = this.isFeedPanelOpen();
|
|
|
|
if(bOldValue != bNewValue) {
|
|
this.spot.onResize();
|
|
sMapAction = sMapAction || 'panTo';
|
|
switch(sMapAction) {
|
|
case 'none': break;
|
|
case 'panTo': this.spot.tmp('map').panBy([(this.isFeedPanelOpen()?1:-1)*this.spot.tmp('$Feed').outerWidth(true)/2, 0], {duration: 0.5}); break;
|
|
case 'panToInstant': this.spot.tmp('map').panBy([(this.isFeedPanelOpen()?1:-1)*this.spot.tmp('$Feed').outerWidth(true)/2, 0]); break;
|
|
case 'fitBounds': this.spot.tmp('map').fitBounds(this.spot.tmp('track').getBounds(), {paddingTopLeft: L.point(5, this.spot.tmp('marker_size').height + 5), paddingBottomRight: L.point(this.spot.tmp('$Feed').outerWidth(true) + 5, 5)}); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
isFeedPanelOpen() {
|
|
return this.spot.tmp('$Projects').hasClass('with-feed');
|
|
}
|
|
|
|
toggleSettingsPanel(bShow, sMapAction) {
|
|
let bOldValue = this.isSettingsPanelOpen();
|
|
this.spot.tmp('$Projects').toggleClass('with-settings', (typeof bShow === 'undefined')?null:bShow);
|
|
let bNewValue = this.isSettingsPanelOpen();
|
|
|
|
if(bOldValue != bNewValue) {
|
|
this.spot.onResize();
|
|
sMapAction = sMapAction || 'panTo';
|
|
switch(sMapAction) {
|
|
case 'none': break;
|
|
case 'panTo': this.spot.tmp('map').panBy([(this.isSettingsPanelOpen()?-1:1)*this.spot.tmp('$Settings').outerWidth(true)/2, 0], {duration: 0.5}); break;
|
|
case 'panToInstant': this.spot.tmp('map').panBy([(this.isSettingsPanelOpen()?-1:1)*this.spot.tmp('$Settings').outerWidth(true)/2, 0]); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
isSettingsPanelOpen() {
|
|
return this.spot.tmp('$Projects').hasClass('with-settings');
|
|
}
|
|
|
|
updateSettingsPanel(asLastUpdate) {
|
|
var $LastUpdate = this.spot.tmp('$Settings').find('#last_update').toggle(this.spot.vars(['project', 'mode']) == this.spot.consts.modes.blog && asLastUpdate.unix_time > 0);
|
|
$LastUpdate.find('abbr')
|
|
.attr('title', asLastUpdate.formatted_time)
|
|
.text(this.spot.lang('last_update')+' '+asLastUpdate.relative_time);
|
|
}
|
|
|
|
isLightboxOpen() {
|
|
return $('#lightbox').is(':visible');
|
|
}
|
|
|
|
isMobile() {
|
|
return $('#mobile').is(':visible');
|
|
}
|
|
|
|
|
|
initProject(sProjectCodeName, oFocusPost){
|
|
this.spot.tmp('first_exec', false);
|
|
this.spot.vars('project', this.spot.vars(['projects', sProjectCodeName]));
|
|
|
|
//Page Title
|
|
this.spot.setPageTitle(this.spot.vars(['project', 'name']));
|
|
this.spot.tmp('$Title').find('#project_name').text(this.spot.vars(['project', 'name']));
|
|
|
|
//Load Track & Markers
|
|
$.when(
|
|
//Markers: Spot Messages & Medias
|
|
this.spot.get(
|
|
'markers',
|
|
function(){},
|
|
{id_project: this.spot.vars(['project', 'id'])},
|
|
function(e){console.log(e);}
|
|
),
|
|
//Project Geojson: Hike track
|
|
$.ajax({
|
|
dataType: 'json',
|
|
url: this.spot.vars(['project', 'geofilepath']),
|
|
mimeType: 'application/json'
|
|
})
|
|
).done((aoMessages, aoTracks) => {
|
|
var asData = aoMessages[0]['data'];
|
|
this.setMapLayers(asData['maps']);
|
|
this.initSpotMessages(asData['messages'], aoTracks[0], asData['medias']);
|
|
this.updateSettingsPanel(asData['last_update']);
|
|
});
|
|
|
|
//Show/Hide Poster Panel
|
|
var bHistoMode = (this.spot.vars(['project', 'mode']) == this.spot.consts.modes.histo);
|
|
this.spot.tmp('$Poster').toggleClass('histo-mode', bHistoMode);
|
|
|
|
//Feed auto-update
|
|
this.updateFeed(true, false, ()=>{this.focusOnPost(oFocusPost);});
|
|
this.spot.tmp('simple-bar').getScrollElement().scrollTop = 0;
|
|
if(!bHistoMode) this.onAutoUpdate(true);
|
|
}
|
|
|
|
initPosts() {
|
|
//Add post form
|
|
this.spot.tmp('$Poster').append(this.getPost({type: 'poster', formatted_time: '', relative_time: this.spot.lang('post_new_message')}));
|
|
|
|
//Add archived Project Notice
|
|
this.spot.tmp('$Poster').append(this.getPost({type: 'archived', headerless: true}));
|
|
|
|
//Auto-adjust text area height
|
|
autosize($('#post'));
|
|
|
|
//Add post Event handling
|
|
$('#submit').on('click', () => {
|
|
if(this.spot.tmp('$Poster').checkForm()) {
|
|
this.spot.get(
|
|
'add_post',
|
|
() => {
|
|
$('#post').val('');
|
|
this.onAutoUpdate();
|
|
},
|
|
{
|
|
id_project: this.spot.vars(['project', 'id']),
|
|
name: $('#name').val(),
|
|
content: $('#post').val()
|
|
}
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
initSettings(){
|
|
//Scrollbar
|
|
new SimpleBar($('#settings-sections-scrollbox')[0]);
|
|
|
|
//Feedback display function
|
|
let settingsFeedback = (sType, sMsg) => {
|
|
$('<p>', {'class': sType})
|
|
.append($('<i>', {'class':'fa push fa-'+sType}))
|
|
.append(sMsg)
|
|
.appendTo($('#settings-feedback'))
|
|
.slideDown('fast')
|
|
.delay(5000)
|
|
.slideUp('fast', function() {$(this).remove();});
|
|
};
|
|
|
|
//Newsletter Subscription
|
|
$('#nl_btn').on('click', (oEvent) => {
|
|
var sAction = $(oEvent.currentTarget).prop('name');
|
|
var sEmail = $('#email').val();
|
|
var regexEmail = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
if(!regexEmail.test(sEmail)) settingsFeedback('error', this.spot.lang('nl_invalid_email'));
|
|
else {
|
|
this.spot.get(
|
|
sAction,
|
|
(asData, sDesc) => {
|
|
settingsFeedback('success', sDesc);
|
|
this.spot.vars('user', asData);
|
|
this.setUserInterface();
|
|
},
|
|
{'email': sEmail, 'name': $('#name').val()},
|
|
(sDesc) => {settingsFeedback('error', sDesc);},
|
|
(sState) => {
|
|
var bLoading = (sState=='start');
|
|
$('#nl_btn')
|
|
.prop('disabled', bLoading)
|
|
.toggleClass('loading', bLoading);
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
//Admin Access
|
|
$('#admin_link').toggle(this.spot.checkClearance(this.spot.consts.clearances.admin));
|
|
|
|
//Twink interface with user data
|
|
this.setUserInterface();
|
|
}
|
|
|
|
setUserInterface() {
|
|
var asUserInfo = this.spot.vars('user');
|
|
if(asUserInfo.id_user) { //Subscribed User
|
|
$('#email').val(asUserInfo.email).prop('disabled', true);
|
|
$('#nl_btn').attr({name: 'unsubscribe', title: this.spot.lang('nl_unsubscribe'), 'class':'unsubscribe'});
|
|
$('#nl_desc').text(this.spot.lang('nl_subscribed_desc'));
|
|
|
|
//Populate nickname
|
|
if(asUserInfo.name) $('#name').val(asUserInfo.name);
|
|
}
|
|
else { //Unsubscribed User
|
|
$('#email').val('').prop('disabled', false);
|
|
$('#nl_btn').attr({name: 'subscribe', title: this.spot.lang('nl_subscribe'), 'class':'subscribe'});
|
|
$('#nl_desc').text(this.spot.lang('nl_unsubscribed_desc'));
|
|
}
|
|
}
|
|
|
|
getElevWidth() {
|
|
var
|
|
iPageWidth = this.spot.tmp('$Projects').width(),
|
|
iFeedPanelWidth = this.isFeedPanelOpen()?this.spot.tmp('$Feed').outerWidth(true):0,
|
|
iSettingsPanelWidth = this.isSettingsPanelOpen()?this.spot.tmp('$Settings').outerWidth(true):0,
|
|
iLegendWidth = $('.leaflet-bottom.leaflet-left > .leaflet-control-layers').outerWidth(true),
|
|
oElevRightMarging = parseInt($('.leaflet-bottom.leaflet-right > .leaflet-control-scale').css('margin-right').slice(0, -2));
|
|
|
|
return iPageWidth - iFeedPanelWidth - iSettingsPanelWidth - iLegendWidth - oElevRightMarging;
|
|
}
|
|
|
|
setMapLayers(asLayers) {
|
|
this.spot.tmp('layers', {});
|
|
$.each(asLayers, (iKey, asLayer) => {
|
|
var sMapName = this.spot.lang('map_'+asLayer.codename);
|
|
var asMapOptions = {
|
|
tileSize: parseInt(asLayer.tile_size),
|
|
zoomOffset: (parseInt(asLayer.tile_size) / -256 + 1),
|
|
minZoom: parseInt(asLayer.min_zoom),
|
|
maxZoom: parseInt(asLayer.max_zoom),
|
|
attribution: asLayer.attribution
|
|
}
|
|
if(asLayer.token != '') asMapOptions.token = asLayer.token;
|
|
|
|
this.spot.tmp('layers')[sMapName] = L.tileLayer(asLayer.pattern, asMapOptions);
|
|
});
|
|
}
|
|
|
|
initSpotMessages(aoMessages, aoTracks, aoMedias) {
|
|
var bIsMobile = this.isMobile();
|
|
|
|
//Map
|
|
var oMap = L.map(this.spot.tmp('$Map')[0], {
|
|
layers: [this.spot.tmp('layers')[this.spot.lang('map_satellite')]],
|
|
attributionControl: false,
|
|
zoomControl: false
|
|
});
|
|
this.spot.tmp('map', oMap);
|
|
|
|
//Controls: Settings Panel
|
|
var oSettingsPanel = L.control({position: bIsMobile?'bottomleft':'topleft'});
|
|
var $SettingsButton = $('#settings-button').clone();
|
|
$SettingsButton.on('click', () => {this.toggleSettingsPanel();});
|
|
oSettingsPanel.onAdd = function(oMap) {return $SettingsButton[0];};
|
|
|
|
//Project Title
|
|
var oTitle = L.control({position: bIsMobile?'bottomleft':'topleft'});
|
|
var $Title = $('#title').clone();
|
|
oTitle.onAdd = function(oMap) {return $Title[0];};
|
|
|
|
if(bIsMobile) {
|
|
oTitle.addTo(oMap);
|
|
oSettingsPanel.addTo(oMap);
|
|
}
|
|
else {
|
|
oSettingsPanel.addTo(oMap);
|
|
oTitle.addTo(oMap);
|
|
}
|
|
|
|
//Controls: Feed Panel
|
|
var oFeedPanel = L.control({position: bIsMobile?'bottomright':'topright'});
|
|
var $FeedButton = $('#feed-button').clone();
|
|
$FeedButton.click(() => {this.toggleFeedPanel();});
|
|
oFeedPanel.onAdd = function(oMap) {return $FeedButton[0];};
|
|
oFeedPanel.addTo(oMap);
|
|
|
|
//Controls: Legend
|
|
if(!bIsMobile) {
|
|
var oLegend = L.control({position: 'bottomleft'});
|
|
oLegend.onAdd = function(oMap) {return $('#legend').clone()[0];};
|
|
oLegend.addTo(oMap);
|
|
}
|
|
|
|
//Controls: Projects
|
|
var $Labels = $('<div>', {'class': 'leaflet-control-layers-base'});
|
|
$.each(this.spot.vars('projects'), (sCodeName, asProject) => {
|
|
var asRadioAttrs = {'type': 'radio', 'class': 'leaflet-control-layers-selector', 'name':'project', 'value': sCodeName};
|
|
if(asProject.id == this.spot.vars(['project', 'id'])) asRadioAttrs.checked = 'checked';
|
|
var $Radio = $('<input>', asRadioAttrs).on('change', (oEvent) => {
|
|
this.toggleSettingsPanel(false);
|
|
this.spot.setHash(this.spot.vars('page'), [$(oEvent.currentTarget).val()]);
|
|
});
|
|
|
|
var $Label = $('<label>').append($('<div>')
|
|
.append($Radio)
|
|
.append($('<span>').text(' '+asProject.name))
|
|
.append($('<a>', {'class':'fa fa-download push-left', href:asProject.gpxfilepath, title:this.spot.lang('track_download')}).on('click', (oEvent) => {oEvent.stopPropagation();}))
|
|
);
|
|
$Labels.append($Label);
|
|
});
|
|
$('#settings-projects').empty().append($Labels);
|
|
|
|
//Controls: Scale
|
|
L.control.scale({imperial: false, position: bIsMobile?'topright':'bottomright'}).addTo(oMap);
|
|
|
|
//Controls: Elevation
|
|
if(!bIsMobile) {
|
|
var aoElevTracks = {type: 'FeatureCollection', features:[], properties: {summary: 'trackType'}};
|
|
var aoLegend = {trackType: {}};
|
|
for(var i in aoTracks.features) {
|
|
if(aoTracks.features[i].properties.type != 'hitchhiking') {
|
|
//Feature
|
|
var oTrack = jQuery.extend(true, {}, aoTracks.features[i]);
|
|
var sType = aoTracks.features[i].properties.type;
|
|
oTrack.properties.attributeType = sType;
|
|
aoElevTracks.features.push(oTrack);
|
|
|
|
//Legend
|
|
aoLegend.trackType[sType] = {text: this.spot.lang('track_'+sType), color: this.spot.tmp(['track-type-styles', sType, 'color'])};
|
|
}
|
|
}
|
|
var oElev = L.control.heightgraph({
|
|
position: 'bottomright',
|
|
expand: false,
|
|
width: this.getElevWidth(),
|
|
height: 200,
|
|
margins: {
|
|
top: 10,
|
|
right: 30,
|
|
bottom: 55,
|
|
left: 50
|
|
},
|
|
translation: {
|
|
distance: this.spot.lang('distance'),
|
|
elevation: this.spot.lang('elevation'),
|
|
segment_length: this.spot.lang('segment_length'),
|
|
type: this.spot.lang('type'),
|
|
legend: this.spot.lang('legend')
|
|
},
|
|
xTicks: 3,
|
|
yTicks: 2,
|
|
mappings: aoLegend,
|
|
highlightStyle: {
|
|
weight: this.spot.tmp(['track-type-styles', 'main', 'weight']),
|
|
opacity: 1,
|
|
color: '#326526'
|
|
},
|
|
expandCallback: (bExpanded) => {
|
|
$('.leaflet-control-scale').toggle(!bExpanded);
|
|
let iElevWidth = this.getElevWidth();
|
|
if(iElevWidth != this.spot.tmp('elev').options.width) this.spot.tmp('elev').resize({width:iElevWidth});
|
|
}
|
|
});
|
|
oElev.addTo(oMap);
|
|
oElev.addData([aoElevTracks]);
|
|
this.spot.tmp('elev', oElev);
|
|
this.spot.tmp('elev-data', [aoElevTracks]);
|
|
}
|
|
|
|
//Controls: Tiles (layers): Add & Move to Settings Panel
|
|
L.control.layers(this.spot.tmp('layers'), null, {position: 'topleft'}).addTo(oMap);
|
|
$('#layers').empty().append($('.leaflet-control-layers-list .leaflet-control-layers-base'));
|
|
|
|
//Actual Tracks: Track with corresponding colors
|
|
var oActualTracks = L.geoJson(aoTracks, {
|
|
style: (oTrack) => {
|
|
return this.spot.tmp(['track-type-styles', oTrack.properties.type]);
|
|
}
|
|
}).addTo(oMap);
|
|
|
|
//"Hover" Tracks: Wider track (thus hover area) to avoid flickering popups
|
|
this.spot.tmp('track', L.geoJson(aoTracks, {
|
|
style: {weight: 20, opacity: 0},
|
|
onEachFeature: (feature, oLayer) => {
|
|
var asProperties = feature.properties;
|
|
var $Tooltip = $('<div>', {'class':'track_tooltip'});
|
|
|
|
//Title
|
|
$('<h1>', {'title':asProperties.name})
|
|
.addIcon('fa-track-'+asProperties.type+' fa-fw', true)
|
|
.append(asProperties.name)
|
|
.appendTo($Tooltip);
|
|
|
|
//Description
|
|
var $TooltipBody = $('<div>', {'class':'body'}).appendTo($Tooltip);
|
|
if(asProperties.Name != asProperties.description && asProperties.description != '') {
|
|
$('<p>', {'class':'description', 'title':asProperties.description}).text(asProperties.description).appendTo($TooltipBody);
|
|
}
|
|
|
|
//Calculate Details
|
|
var aiCoords = feature.geometry.coordinates;
|
|
if(aiCoords && asProperties.type != 'hitchhiking') {
|
|
var iDistance = 0, iElevDrop = 0, iElevGain = 0, iTime = 0;
|
|
|
|
//Track duration (in hours)
|
|
for (var i = 1; i < aiCoords.length; i++) {
|
|
var oCurrPoint = new L.LatLng(aiCoords[i][1], aiCoords[i][0]);
|
|
var oPrevPoint = new L.LatLng(aiCoords[i - 1][1], aiCoords[i - 1][0]);
|
|
var iElevDelta = aiCoords[i][2] - aiCoords[i - 1][2];
|
|
var iSegDistance = oCurrPoint.distanceTo(oPrevPoint);
|
|
|
|
iDistance += iSegDistance;
|
|
iElevDrop += Math.min(iElevDelta, 0);
|
|
iElevGain += Math.max(iElevDelta, 0);
|
|
|
|
var iSpeedCorrecRatio = 0;
|
|
var iAngle = iElevDelta / iSegDistance;
|
|
if(iAngle < -1) iSpeedCorrecRatio = 0.5;
|
|
else if(iAngle < -0.2) iSpeedCorrecRatio = 1.25;
|
|
else if(iAngle < 0.1) iSpeedCorrecRatio = 1;
|
|
else if(iAngle < 0.25) iSpeedCorrecRatio = 0.85;
|
|
else if(iAngle < 0.5) iSpeedCorrecRatio = 0.6;
|
|
else if(iAngle < 1) iSpeedCorrecRatio = 0.5;
|
|
else iSpeedCorrecRatio = 0.25;
|
|
iTime += iSegDistance / 1000 * iSpeedCorrecRatio / 3.5 ; //default speed: 3.5km/h
|
|
}
|
|
|
|
//Conversion of hours into natural language
|
|
var sDuration = this.spot.getNaturalDuration(iTime);
|
|
|
|
//Tooltip details info
|
|
$TooltipBody
|
|
.append($('<div>', {'class':'separator'}))
|
|
.append($('<div>', {'class':'details'})
|
|
.append($('<p>', {'class':'detail'}).addIcon('fa-distance fa-fw', true).append(Math.round(iDistance/1000)+'km'))
|
|
.append($('<p>', {'class':'detail'}).addIcon('fa-time fa-fw', true).append(sDuration))
|
|
.append($('<p>', {'class':'detail'}).addIcon('fa-elev-gain fa-fw', true).append(iElevGain+'m'))
|
|
.append($('<p>', {'class':'detail'}).addIcon('fa-elev-drop fa-fw', true).append(iElevDrop+'m'))
|
|
);
|
|
|
|
//Trail Start/End
|
|
this.spot.tmp(['trail-markers', asProperties.name], {
|
|
'start' : L.marker(new L.latLng(aiCoords[0][1], aiCoords[0][0]), {icon: this.getDivIcon('track-start')}),
|
|
'end' : L.marker(new L.latLng(aiCoords[aiCoords.length - 1][1], aiCoords[aiCoords.length - 1][0]), {icon: this.getDivIcon('track-end')})
|
|
});
|
|
|
|
oLayer
|
|
.bindPopup($Tooltip[0])
|
|
.on('click', (e) => {
|
|
this.toggleSoftPopup(e, 'open');
|
|
})
|
|
.on('popupclose', (e) => {
|
|
this.toggleSoftPopup(e, 'close');
|
|
});
|
|
}
|
|
}
|
|
}).addTo(oMap));
|
|
|
|
//Last message
|
|
var oLastMsg = (aoMessages.length > 0)?aoMessages[aoMessages.length-1]:{};
|
|
|
|
//Centering map
|
|
var iFeedPanelWidth = bIsMobile?0:parseInt(this.spot.tmp('$Feed').outerWidth(true));
|
|
var iTitleHeight = bIsMobile?this.spot.tmp('$Title').outerHeight(true):0;
|
|
if(
|
|
this.spot.vars(['project', 'mode']) == this.spot.consts.modes.blog &&
|
|
!$.isEmptyObject(oLastMsg) &&
|
|
this.spot.getHash()[2] != 'message'
|
|
) {
|
|
//Zoom on last message
|
|
oMap.setView(L.latLng(oLastMsg.latitude, oLastMsg.longitude), 15);
|
|
oMap.panBy([iFeedPanelWidth/2, /*bIsMobile?(this.spot.tmp('$Title').outerHeight(true) * -1/2):*/0]);
|
|
}
|
|
else {
|
|
oMap.fitBounds(
|
|
this.spot.tmp('track').getBounds(),
|
|
{
|
|
paddingTopLeft: L.point(5, 5 + this.spot.tmp('marker_size').height), //So that the first marker isn't off screen
|
|
paddingBottomRight: L.point(5 + iFeedPanelWidth, 5 + iTitleHeight)
|
|
}
|
|
);
|
|
}
|
|
|
|
//Add Spot messages
|
|
this.addSpotMarkers(aoMessages);
|
|
|
|
//Add Medias
|
|
this.addMediaMarkers(aoMedias)
|
|
|
|
//Open tooltip on latest message in mobile mode
|
|
if(
|
|
!$.isEmptyObject(oLastMsg) &&
|
|
this.spot.vars(['project', 'mode']) == this.spot.consts.modes.blog &&
|
|
(!oLastMsg.medias || oLastMsg.medias.length < 3) &&
|
|
this.isMobile()
|
|
) this.spot.tmp(['markers', oLastMsg.id_message]).openPopup();
|
|
|
|
/*
|
|
this.spot.tmp('tracks', aoTracks);
|
|
next(24, 0, 0, 5);
|
|
*/
|
|
}
|
|
|
|
addSpotMarkers(aoMessages) {
|
|
let iWorkSpaceMinWidth = this.isMobile()?this.spot.tmp('$Projects').width():(this.spot.tmp('$Projects').width() - this.spot.tmp('$Feed').outerWidth(true) - this.spot.tmp('$Settings').outerWidth(true));
|
|
|
|
$.each(aoMessages, (iKey, oMsg) => {
|
|
|
|
//Tooltip
|
|
let $Tooltip = $('<div>', {'class':'info-window'})
|
|
.append($('<h1>')
|
|
.addIcon('fa-message fa-lg', true)
|
|
.append($('<span>').text(this.spot.lang('post_message')+' '+this.spot.lang('counter', oMsg.displayed_id)))
|
|
.append($('<span>', {'class':'message-type'}).text('('+oMsg.type+')'))
|
|
)
|
|
.append($('<div>', {'class':'separator'}))
|
|
.append($('<p>', {'class':'coordinates'})
|
|
.addIcon('fa-coords fa-fw fa-lg', true)
|
|
.append(this.getGoogleMapsLink(oMsg)))
|
|
.append($('<p>', {'class':'time'})
|
|
.addIcon('fa-time fa-fw fa-lg', true)
|
|
.append(oMsg.formatted_time+(this.spot.vars(['project', 'mode'])==this.spot.consts.modes.blog?' ('+oMsg.relative_time+')':'')));
|
|
|
|
//Tooltip: Time Zone
|
|
if(oMsg.formatted_time_local != oMsg.formatted_time) {
|
|
$Tooltip.append($('<p>', {'class':'timezone'})
|
|
.addIcon('fa-timezone fa-fw fa-lg', true)
|
|
.append(this.spot.lang('local_time', this.getRelativeTime(oMsg.formatted_time_local, oMsg.day_offset))));
|
|
}
|
|
|
|
//Tooltip: Weather
|
|
if(oMsg.weather_icon && oMsg.weather_icon!='unknown') {
|
|
$Tooltip.append($('<p>', {'class':'weather', 'title':(oMsg.weather_cond==''?'':this.spot.lang(oMsg.weather_cond))})
|
|
.addIcon('fa-'+oMsg.weather_icon+' fa-fw fa-lg', true)
|
|
.append(oMsg.weather_temp+'°C')
|
|
);
|
|
}
|
|
|
|
//Marker
|
|
let oMarker = L.marker(
|
|
L.latLng(oMsg.latitude, oMsg.longitude),
|
|
{
|
|
riseOnHover: true,
|
|
icon: this.getDivIcon('message-in fa-rotate-270'),
|
|
$Tooltip: $Tooltip,
|
|
medias: oMsg.medias
|
|
}
|
|
)
|
|
.bindPopup(
|
|
(e) => {
|
|
//Tooltip: Medias: Set on the fly to avoid resource load
|
|
let $Tooltip = e.options.$Tooltip;
|
|
let $Medias = $Tooltip.find('.medias');
|
|
if(e.options.medias && $Medias.length == 0) {
|
|
$Medias = $('<div>', {'class':'medias'});
|
|
$.each(e.options.medias, (iKey, asMedia) => {
|
|
$Medias.append(this.getMediaLink(asMedia, 'marker'));
|
|
});
|
|
$Tooltip.append($Medias);
|
|
}
|
|
return $Tooltip[0];
|
|
},
|
|
{
|
|
maxWidth: iWorkSpaceMinWidth,
|
|
autoPan: false,
|
|
closeOnClick: true,
|
|
offset: new L.Point(0, -30)
|
|
}
|
|
)
|
|
.addTo(this.spot.tmp('map'));
|
|
|
|
this.spot.tmp(['markers', oMsg.id_message], oMarker);
|
|
});
|
|
}
|
|
|
|
addMediaMarkers(aoMedias) {
|
|
let iWorkSpaceMinWidth = this.isMobile()?this.spot.tmp('$Projects').width():(this.spot.tmp('$Projects').width() - this.spot.tmp('$Feed').outerWidth(true) - this.spot.tmp('$Settings').outerWidth(true));
|
|
|
|
$.each(aoMedias, (iKey, oMedia) => {
|
|
|
|
//Tooltip
|
|
let $Tooltip = $('<div>', {'class':'info-window'})
|
|
.append($('<h1>')
|
|
.addIcon('fa-'+oMedia.subtype+' fa-lg', true)
|
|
.append($('<span>').text(this.spot.lang(oMedia.subtype)+' '+this.spot.lang('counter', oMedia.displayed_id || oMedia.id_media)))
|
|
)
|
|
.append($('<div>', {'class':'separator'}))
|
|
.append($('<p>', {'class':'time'})
|
|
.addIcon('fa-time fa-fw fa-lg', true)
|
|
.append(oMedia.formatted_time+(this.spot.vars(['project', 'mode'])==this.spot.consts.modes.blog?' ('+oMedia.relative_time+')':'')));
|
|
|
|
//Tooltip: Time Zone
|
|
if(oMedia.formatted_time_local != oMedia.formatted_time) {
|
|
$Tooltip.append($('<p>', {'class':'timezone'})
|
|
.addIcon('fa-timezone fa-fw fa-lg', true)
|
|
.append(this.spot.lang('local_time', getRelativeTime(oMedia.formatted_time_local, oMedia.day_offset))));
|
|
}
|
|
|
|
//Tooltip: Altitude
|
|
if(oMedia.altitude) {
|
|
$Tooltip.append($('<p>', {'class':'altitude'})
|
|
.addIcon('fa-altitude fa-fw fa-lg', true)
|
|
.append(oMedia.altitude+'m'));
|
|
}
|
|
|
|
//Marker
|
|
let oMarker = L.marker(
|
|
L.latLng(oMedia.latitude, oMedia.longitude),
|
|
{
|
|
riseOnHover: true,
|
|
icon: this.getDivIcon(oMedia.subtype),
|
|
$Tooltip: $Tooltip,
|
|
medias: [oMedia]
|
|
}
|
|
)
|
|
.bindPopup(
|
|
(e) => {
|
|
//Tooltip: Medias: Set on the fly to avoid resource load
|
|
let $Tooltip = e.options.$Tooltip;
|
|
let $Medias = $Tooltip.find('.medias');
|
|
if(e.options.medias && $Medias.length == 0) {
|
|
$Medias = $('<div>', {'class':'medias'});
|
|
$.each(e.options.medias, (iKey, asMedia) => {
|
|
$Medias.append(this.getMediaLink(asMedia, 'marker'));
|
|
});
|
|
$Tooltip.append($Medias);
|
|
}
|
|
return $Tooltip[0];
|
|
},
|
|
{
|
|
maxWidth: iWorkSpaceMinWidth,
|
|
autoPan: false,
|
|
closeOnClick: true,
|
|
offset: new L.Point(0, -30)
|
|
}
|
|
)
|
|
.addTo(this.spot.tmp('map'));
|
|
|
|
oMarker.on('mouseover', function(e) {this.openPopup();});
|
|
});
|
|
}
|
|
|
|
toggleSoftPopup(e, sMode) {
|
|
switch(sMode) {
|
|
case 'open':
|
|
var oLatLng = L.GeometryUtil.closest(this.spot.tmp('map'), e.sourceTarget, e.latlng);
|
|
var oPopup = e.target.getPopup();
|
|
if(!oPopup.isOpen()) oPopup.setLatLng(oLatLng).openOn(this.spot.tmp('map'));
|
|
|
|
var asTrailMarkers = this.spot.tmp(['trail-markers', e.target.feature.properties.name]);
|
|
var oPointStart = this.spot.tmp('map').latLngToLayerPoint(asTrailMarkers.start.getLatLng());
|
|
var oPointEnd = this.spot.tmp('map').latLngToLayerPoint(asTrailMarkers.end.getLatLng());
|
|
if(oPointStart.distanceTo(oPointEnd) > 200) {
|
|
asTrailMarkers.start.addTo(this.spot.tmp('map'));
|
|
asTrailMarkers.end.addTo(this.spot.tmp('map'));
|
|
}
|
|
break;
|
|
case 'close':
|
|
//e.target.closePopup();
|
|
var asTrailMarkers = this.spot.tmp(['trail-markers', e.target.feature.properties.name]);
|
|
asTrailMarkers.start.remove();
|
|
asTrailMarkers.end.remove();
|
|
break;
|
|
}
|
|
}
|
|
|
|
getDivIcon(sIcon) {
|
|
return L.divIcon({
|
|
className: '',
|
|
html: '<span class="fa-stack"><i class="fa fa-message fa-stack-2x"></i><i class="fa fa-'+sIcon+' fa-stack-1x"></i></span>',
|
|
iconSize: [this.spot.tmp('marker_size').width, this.spot.tmp('marker_size').height],
|
|
iconAnchor: [this.spot.tmp('marker_size').width / 2, this.spot.tmp('marker_size').height] //position from icon's top left corner (iconAnchor = marker's position)
|
|
});
|
|
}
|
|
|
|
onFeedScroll(oEvent) {
|
|
var $Box = $(oEvent.currentTarget);
|
|
var $BoxContent = $Box.find('.simplebar-content');
|
|
if(($Box.scrollTop() + $(window).height()) / $BoxContent.height() >= 0.8) this.updateFeed();
|
|
}
|
|
|
|
onAutoUpdate(bStart) {
|
|
bStart = bStart || false;
|
|
if(!bStart) this.checkNewFeed();
|
|
this.setFeedUpdateTimer(60, this.onAutoUpdate);
|
|
}
|
|
|
|
checkNewFeed() {
|
|
this.spot.get(
|
|
'new_feed',
|
|
(asData) => {
|
|
let iItemCount = Object.keys(asData.feed).length;
|
|
if(iItemCount > 0) this.spot.tmp('ref_id_first', asData.ref_id_first);
|
|
|
|
//Feed
|
|
let $Posts = $('<div>');
|
|
$.each(asData.feed, (iKey, asPost) => {
|
|
$Posts.append(this.getPost(asPost));
|
|
});
|
|
this.spot.tmp('$PostList').prepend($Posts.children());
|
|
|
|
//Markers
|
|
this.addSpotMarkers(asData.messages);
|
|
|
|
//Message Last Update
|
|
this.updateSettingsPanel(asData.last_update);
|
|
}, {
|
|
id_project: this.spot.vars(['project', 'id']),
|
|
id: this.spot.tmp('ref_id_first')
|
|
}
|
|
);
|
|
}
|
|
|
|
setFeedUpdateTimer(iSeconds, fCallback) {
|
|
if(typeof this.spot.tmp('update_timer') != 'undefined') clearTimeout(this.spot.tmp('update_timer'));
|
|
if(iSeconds >= 0) this.spot.tmp('update_timer', setTimeout(fCallback.bind(this), iSeconds * 1000));
|
|
}
|
|
|
|
updateFeed(bFirstChunk, bDiscrete, fCallback) {
|
|
bFirstChunk = bFirstChunk || false;
|
|
bDiscrete = bDiscrete || false;
|
|
fCallback = fCallback || function(){};
|
|
|
|
//First/Last Item displayed on the feed
|
|
if(bFirstChunk) this.spot.tmp('ref_id_last', 0);
|
|
|
|
if(this.spot.tmp('updatable')) {
|
|
if(!this.spot.tmp('out-of-data') || bFirstChunk) {
|
|
this.spot.tmp('updatable', false);
|
|
if(!bDiscrete) $('#loading').show();
|
|
|
|
var $Posts = $('<div>');
|
|
var a$Medias = [];
|
|
|
|
this.spot.get(
|
|
'next_feed',
|
|
(asData) => {
|
|
$('#loading').hide();
|
|
var iItemCount = Object.keys(asData.feed).length;
|
|
|
|
//Update pointers
|
|
this.spot.tmp('out-of-data', iItemCount < this.spot.vars('chunk_size'));
|
|
if(iItemCount > 0) {
|
|
this.spot.tmp('ref_id_last', asData.ref_id_last);
|
|
if(bFirstChunk === true) this.spot.tmp('ref_id_first', asData.ref_id_first);
|
|
}
|
|
|
|
//Add posts
|
|
$.each(asData.feed, (iKey, asPost) => {
|
|
var $Post = this.getPost(asPost);
|
|
$Posts.append($Post);
|
|
if(asPost.type == 'media' && this.isLightboxOpen()) a$Medias.push($Post.find('.media-link'));
|
|
});
|
|
if(bFirstChunk === true) this.spot.tmp('$PostList').empty();
|
|
this.spot.tmp('$PostList').append($Posts.children());
|
|
this.spot.tmp('$PostList').find('img').waitForImages(true).done(fCallback);
|
|
|
|
//Add media to lightbox
|
|
$.each(a$Medias, (iKey, $Media) => {
|
|
lightbox.addToAlbum($Media);
|
|
});
|
|
if(a$Medias.length > 0) lightbox.updateDetails();
|
|
|
|
this.spot.tmp('updatable', true);
|
|
}, {
|
|
id_project: this.spot.vars(['project', 'id']),
|
|
id: this.spot.tmp('ref_id_last')
|
|
}
|
|
);
|
|
}
|
|
}
|
|
else if(bFirstChunk) this.setFeedUpdateTimer(0.2, () => {this.updateFeed(bFirstChunk, bDiscrete, fCallback);});
|
|
}
|
|
|
|
focusOnPost(oFocusPost) {
|
|
if(oFocusPost) {
|
|
if(oFocusPost.type=='message' && (!this.spot.tmp(['markers', oFocusPost.id]) || !this.spot.tmp('map'))) {
|
|
setTimeout(() => {this.focusOnPost(oFocusPost);}, 100);
|
|
}
|
|
else {
|
|
var sElemId = '#'+oFocusPost.type+'-'+oFocusPost.id;
|
|
var $Post = this.spot.tmp('$PostList').find(sElemId);
|
|
var bGetToTop = (this.spot.tmp('$PostList').height() - (($Post.length > 0)?$Post.position().top:0) >= $(window).height());
|
|
if($Post.length > 0 && (bGetToTop || this.spot.tmp('out-of-data'))) {
|
|
this.goToPost(oFocusPost);
|
|
if(oFocusPost.type=='media' || oFocusPost.type=='message') $Post.find('a.drill').click();
|
|
}
|
|
else if(!this.spot.tmp('out-of-data')) this.updateFeed(false, false, () => {this.focusOnPost(oFocusPost);});
|
|
else console.log('Missing element ID '+sElemId);
|
|
|
|
//Reset Hash
|
|
this.spot.flushHash(['post', 'message']);
|
|
}
|
|
}
|
|
}
|
|
|
|
goToPost(oPost) {
|
|
var sElemId = '#'+oPost.type+'-'+oPost.id;
|
|
var $Post = this.spot.tmp('$PostList').find(sElemId);
|
|
if($Post.length > 0) {
|
|
this.spot.tmp('simple-bar').getScrollElement().scrollTop += Math.round(
|
|
$Post.offset().top
|
|
- parseInt($('#feed-panel').css('padding-top'))
|
|
/*
|
|
$Post.position().top
|
|
+ parseInt($Post.css('margin-top'))
|
|
+ this.spot.tmp('$Poster').outerHeight(true)
|
|
+ parseInt($('#feed-panel').css('padding-top'))
|
|
*/
|
|
);
|
|
}
|
|
else console.log('Missing element ID '+sElemId);
|
|
}
|
|
|
|
getRelativeTime(sLocalTime, iOffset) {
|
|
let $Time = $('<span>').text(sLocalTime.substr(-5));
|
|
if(iOffset != '0') $Time.append($('<sup>', {'title': iOffset+' '+this.spot.lang('unit_day')+' ('+sLocalTime.substr(0, 5)+')'}).text(' '+iOffset));
|
|
return $Time.html();
|
|
}
|
|
|
|
getPost(asPost) {
|
|
asPost.headerless = asPost.headerless || false;
|
|
var bLink = false;
|
|
var asHash = this.spot.getHash();
|
|
var sHash = '#'+[asHash.page, asHash.items[0], asPost.type, asPost.id].join(this.spot.consts.hash_sep);
|
|
|
|
var $Post = $('<div>', {'class':'post-item '+asPost.type+(asPost.headerless?' headerless':'')});
|
|
if(asPost.id) $Post.prop('id', asPost.type+'-'+asPost.id);
|
|
|
|
var sRelTime = (asPost.relative_time!='')?((this.spot.vars('project') && this.spot.vars(['project', 'mode'])==this.spot.consts.modes.histo)?asPost.formatted_time.substr(0, 10):asPost.relative_time):'';
|
|
var sAbsTime = asPost.formatted_time;
|
|
var sAbsTimeLocal = asPost.formatted_time_local;
|
|
var bTimeDiff = (sAbsTime && sAbsTimeLocal != sAbsTime);
|
|
var sType = asPost.subtype || asPost.type;
|
|
var $Body = {};
|
|
|
|
switch(asPost.type) {
|
|
case 'message':
|
|
bLink = true;
|
|
$Body = $('<div>', {'class':'body-box'})
|
|
.data('id', asPost.id_message)
|
|
.data('clicked', false)
|
|
.append($('<p>').addIcon('fa-coords', true).append(this.getGoogleMapsLink(asPost)))
|
|
.append($('<p>').addIcon('fa-time', true).append(sAbsTime))
|
|
.append(bTimeDiff?$('<p>').addIcon('fa-timezone', true).append(this.spot.lang('local_time', this.getRelativeTime(sAbsTimeLocal, asPost.day_offset))):'')
|
|
.append($('<a>', {'class':'drill'})
|
|
.append((!asPost.weather_icon || asPost.weather_icon=='unknown')?'':$('<span>', {'class':'weather clickable', 'title':this.spot.lang(asPost.weather_cond)}).addIcon('fa-'+asPost.weather_icon).append($('<span>').text(asPost.weather_temp+'°C')))
|
|
.append($('<img>', {'class':'staticmap clickable', title: this.spot.lang('click_zoom'), src: asPost.static_img_url}))
|
|
.append($('<span>', {'class': 'drill-icon fa-stack clickable'})
|
|
.addIcon('fa-message fa-stack-2x clickable')
|
|
.addIcon('fa-message-in fa-stack-1x fa-rotate-270')
|
|
)
|
|
.on('click', (oEvent) => {
|
|
var $Parent = $(oEvent.currentTarget).parent();
|
|
var oMarker = this.spot.tmp(['markers', $Parent.data('id')]);
|
|
if(this.isMobile()) {
|
|
this.toggleFeedPanel(false, 'panToInstant');
|
|
this.spot.tmp('map').setView(oMarker.getLatLng(), 15);
|
|
}
|
|
else {
|
|
var iOffset = (this.isFeedPanelOpen()?1:-1)*this.spot.tmp('$Feed').outerWidth(true)/2 - (this.isSettingsPanelOpen()?1:-1)*this.spot.tmp('$Settings').outerWidth(true)/2;
|
|
var iRatio = -1 * iOffset / $('body').outerWidth(true);
|
|
this.spot.tmp('map').setOffsetView(iRatio, oMarker.getLatLng(), 15);
|
|
}
|
|
|
|
$Parent.data('clicked', true);
|
|
if(!oMarker.isPopupOpen()) oMarker.openPopup();
|
|
})
|
|
)
|
|
.hover(
|
|
(oEvent) => {
|
|
let oMarker = this.spot.tmp(['markers', $(oEvent.currentTarget).data('id')]);
|
|
if(this.spot.tmp('map') && this.spot.tmp('map').getBounds().contains(oMarker.getLatLng()) && !oMarker.isPopupOpen()) oMarker.openPopup();
|
|
},
|
|
(oEvent) => {
|
|
let $This = $(oEvent.currentTarget);
|
|
let oMarker = this.spot.tmp(['markers', $This.data('id')]);
|
|
if(oMarker && oMarker.isPopupOpen() && !$This.data('clicked')) oMarker.closePopup();
|
|
$This.data('clicked', false);
|
|
}
|
|
);
|
|
break;
|
|
case 'media':
|
|
bLink = true;
|
|
$Body = $('<div>', {'class':'body-box'}).append(this.getMediaLink(asPost, 'post'));
|
|
if(asPost.comment) $Body.find('a.drill').append($('<span>', {'class':'comment'}).text(asPost.comment));
|
|
break;
|
|
case 'post':
|
|
bLink = true;
|
|
var $SigImg = asPost.gravatar?$('<img>').attr({'src':'data:image/png;base64, '+asPost.gravatar, 'width':'24', 'height':'24', 'alt':'--'}):($('<span>').text('-- '));
|
|
$Body = $('<div>')
|
|
.append($('<p>', {'class':'message'}).text(asPost.content))
|
|
.append($('<p>', {'class':'signature'})
|
|
.append($SigImg)
|
|
.append($('<span>').text(asPost.formatted_name))
|
|
);
|
|
break;
|
|
case 'poster':
|
|
$Body = $('<p>', {'class':'message'})
|
|
.append($('<textarea>', {id:'post', name:'post', placeholder:this.spot.lang('post_message'), 'class':'autoExpand', rows:'1'}))
|
|
.append($('<input>', {type:'text', id:'name', name:'name', placeholder:this.spot.lang('post_name')}))
|
|
.append($('<button>', {type:'button', id:'submit', name:'submit', 'aria-label':this.spot.lang('send')}).addIcon('fa-send'));
|
|
break;
|
|
case 'archived':
|
|
$Body = $('<div>')
|
|
.append($('<p>').addIcon('fa-success'))
|
|
.append($('<p>').text(this.spot.lang('mode_histo')));
|
|
break;
|
|
case 'loading':
|
|
$Body = $('<p>', {'class':'flicker'}).addIcon('fa-post');
|
|
break;
|
|
}
|
|
|
|
$Post
|
|
.append($('<div>', {'class':'header'})
|
|
.append($('<span>', {'class':'index'})
|
|
.addIcon('fa-'+sType)
|
|
.append(asPost.displayed_id?' '+this.spot.lang('counter', asPost.displayed_id):'')
|
|
)
|
|
.append($('<span>', {'class':'time', title:bTimeDiff?this.spot.lang('local_time', sAbsTimeLocal):''}).hoverSwap(sRelTime, bTimeDiff?this.spot.lang('your_time', sAbsTime):sAbsTime)))
|
|
.append($('<div>', {'class':'body'}).append($Body));
|
|
|
|
if(bLink) {
|
|
$Post.find('.index').append($('<a>', {'class':'link desktop', 'href':sHash, 'title':this.spot.lang('copy_to_clipboard')})
|
|
.data('url', this.spot.consts.server+sHash)
|
|
.addIcon('fa-link')
|
|
.on('click', ($This) => {
|
|
let $Link = $($This.currentTarget);
|
|
copyTextToClipboard($Link.data('url'));
|
|
$Link.prop('title', this.spot.lang('link_copied')).find('i.fa').addClass('copied');
|
|
$Link.delay(5000).fadeOut(300, () => {
|
|
$Link.prop('title', this.spot.lang('copy_to_clipboard')).find('i.fa').removeClass('copied');
|
|
$Link.fadeIn(300);
|
|
});
|
|
})
|
|
);
|
|
}
|
|
|
|
return $Post;
|
|
}
|
|
|
|
getMediaLink(asData, sType) {
|
|
var bTimeDiff = (asData.posted_on_formatted && asData.posted_on_formatted_local != asData.posted_on_formatted);
|
|
|
|
var $Comment = (!asData.comment || asData.comment == '')?'':
|
|
$('<span>', {'class': 'lb-caption-line comment desktop', 'title': asData.comment})
|
|
.addIcon('fa-post fa-lg fa-fw', true)
|
|
.append($('<span>', {'class':'comment-text'}).text(asData.comment));
|
|
|
|
var $PostedOn =
|
|
$('<span>', {'class': 'lb-caption-line', title: bTimeDiff?this.spot.lang('local_time', asData.posted_on_formatted_local):''})
|
|
.addIcon('fa-upload fa-lg fa-fw', true)
|
|
.append(asData.posted_on_formatted);
|
|
|
|
var $TakenOn = (asData.taken_on == asData.posted_on)?'':
|
|
$('<span>', {'class': 'lb-caption-line', title: bTimeDiff?this.spot.lang('local_time', asData.taken_on_formatted_local):''})
|
|
.addIcon('fa-'+asData.subtype+'-shot fa-lg fa-fw', true)
|
|
.append(asData.taken_on_formatted);
|
|
|
|
var $Title = $('<div>').append($Comment).append(sType=='marker'?$TakenOn:$PostedOn).append(sType=='marker'?$PostedOn:$TakenOn);
|
|
var $Link =
|
|
$('<a>', {
|
|
'class': 'media-link drill',
|
|
'href': asData.media_path,
|
|
'data-lightbox': sType+'-medias',
|
|
'data-type': asData.subtype,
|
|
'data-id': asData.id_media,
|
|
'data-title': $Title.html(),
|
|
'data-orientation': asData.rotate
|
|
})
|
|
.append($('<img>')
|
|
.attr({
|
|
'src': asData.thumb_path,
|
|
'width': asData.width, //set image ratio so that the required space can be reserved
|
|
'height': asData.height,
|
|
'title': this.spot.lang((asData.subtype == 'video')?'click_watch':'click_zoom'),
|
|
'class':'clickable'
|
|
})
|
|
)
|
|
.append($('<span>', {'class': 'drill-icon'}).addIcon('fa-drill-'+asData.subtype));
|
|
|
|
return $Link;
|
|
}
|
|
|
|
getGoogleMapsLink(asInfo) {
|
|
return $('<a>', {
|
|
href:'https://www.google.com/maps/place/'+asInfo.lat_dms+'+'+asInfo.lon_dms+'/@'+asInfo.latitude+','+asInfo.longitude+',10z',
|
|
title: this.spot.lang('see_on_google'),
|
|
target: '_blank',
|
|
rel: 'noreferrer noopener'
|
|
}).text(asInfo.lat_dms+' '+asInfo.lon_dms);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
function next(iCurrTrack, iCurrIndex, iCurrOffset, iCurrZoom) {
|
|
var aoTracks = this.spot.tmp('tracks');
|
|
var aoOffset = {0:[0,0], 1:[-1,0], 2:[-1,1], 3:[0,1], 4:[1,1], 5:[1,0], 6:[1,-1], 7:[0,-1], 8:[-1,-1]};
|
|
|
|
console.log('Getting Track '+iCurrTrack+'/'+(aoTracks.features.length - 1)+', '+
|
|
'Point '+iCurrIndex+'/'+(aoTracks.features[iCurrTrack].geometry.coordinates.length - 1)+', '+
|
|
'Zoom '+iCurrZoom+'/14, '+
|
|
'Offset ['+aoOffset[iCurrOffset][0]+','+aoOffset[iCurrOffset][1]+']');
|
|
|
|
//Position map
|
|
var iLat = aoTracks.features[iCurrTrack].geometry.coordinates[iCurrIndex][1] + aoOffset[iCurrOffset][1] * 0.0347910214271;
|
|
var iLng = aoTracks.features[iCurrTrack].geometry.coordinates[iCurrIndex][0] + aoOffset[iCurrOffset][0] * 0.1016235351560;
|
|
this.spot.tmp('map').setView(L.latLng(iLat, iLng), iCurrZoom);
|
|
|
|
//Go to next
|
|
iCurrOffset++;
|
|
if(iCurrZoom < 13 || iCurrOffset > 8) {
|
|
iCurrOffset = 0;
|
|
iCurrZoom++;
|
|
if(iCurrZoom > 14) {
|
|
iCurrZoom = 5;
|
|
iCurrIndex += 100;
|
|
if(!(iCurrIndex in aoTracks.features[iCurrTrack].geometry.coordinates)) {
|
|
iCurrIndex = 0;
|
|
iCurrTrack++;
|
|
if(!(iCurrTrack in aoTracks.features)) return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
timer = setTimeout(function(){next(iCurrTrack, iCurrIndex, iCurrOffset, iCurrZoom);}, 1000);
|
|
}
|
|
function stop() {
|
|
clearTimeout(timer);
|
|
}
|
|
*/ |