//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: ' %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) => { $('
', {'class': sType})
.append($('', {'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 = $('