//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 = $('

', {'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 = $('', asRadioAttrs).on('change', (oEvent) => { this.toggleSettingsPanel(false); this.spot.setHash(this.spot.vars('page'), [$(oEvent.currentTarget).val()]); }); var $Label = $('