Files
spot/masks/project.html
2019-02-06 18:56:08 +01:00

427 lines
13 KiB
HTML
Executable File

<div id="messages">
<div class="loader fa fa-fw fa-map"></div>
<div id="map"></div>
<div id="feed">
<div id="posts">
<div id="poster"></div>
<div id="posts_list"></div>
<div id="loading"></div>
</div>
</div>
<div id="elems">
<div id="legend" class="leaflet-control-layers leaflet-control leaflet-control-layers-expanded">
<div class="line main">Trajet principal</div>
<div class="line off-track">Variante</div>
<div class="line hitchhiking">Hors rando</div>
</div>
</div>
</div>
<script type="text/javascript" src="script/lightbox.min.js"></script>
<script type="text/javascript">
oSpot.onSamePageMove = function(asHash) {
if(self.tmp('first_exec')) initPage(asHash);
else {
self.tmp('map').remove();
self.tmp('$Map').empty();
self.tmp('map', null);
initProject(asHash.items[0]);
}
return false;
};
oSpot.pageInit = function(asHash) {
self.tmp('first_exec', true);
initPage(asHash);
};
function initPage(asHash) {
if(asHash.items.length==0) self.setHash(asHash.page, [self.vars('default_project_codename')]);
else {
self.tmp('$Map', $('#map'));
self.tmp('updatable', true);
self.tmp('out-of-data', false);
self.tmp('feed_width', $('#feed').outerWidth(true));
self.tmp('map_offset', -1 * self.tmp('feed_width') / $('body').outerWidth(true));
self.tmp('tile_api', '?a=tile&id={id}&z={z}&x={x}&y={y}');
//Assign Track Type Colors
self.tmp('track-type-styles', 'object');
$('#legend').find('.line').each(function(iKey, oLegend){
var $Legend = $(oLegend);
var sTrackType = $Legend.attr('class').replace('line', '').trim();
self.tmp(['track-type-styles', sTrackType], {weight: parseInt($Legend.css('line-height')), color: $Legend.css('border-top-color'), opacity: 1});
});
//Scrollbar
self.tmp('simple-bar', new SimpleBar($('#posts')[0]));
self.tmp('simple-bar').getScrollElement().addEventListener('scroll', onFeedScroll);
//Add "Loading" Post
getPost({type: 'loading', formatted_time: '', relative_time: '', displayed_id: 'Chargement...'}).appendTo($('#loading'));
//project Bootstrap
initProject(asHash.items[0]);
}
}
function initProject(sProjectCodeName){
self.tmp('first_exec', false);
self.vars('project', self.vars(['projects', sProjectCodeName]));
$.when(
//Markers: Spot Messages & Pictures
self.get(
'markers',
function(){},
{project_id: self.vars(['project', 'id'])},
function(e){console.log(e);}
),
//Project Geojson: Hike track
$.ajax({
dataType: 'json',
url: self.vars(['project', 'geofile']),
mimeType: 'application/json'
})
).done(function(aoMessages, aoTracks) {
initSpotMessages(aoMessages[0]['data'], aoTracks[0]);
});
//Posts
initPosts();
}
function initPosts() {
var $Poster = $('#poster').empty();
if(self.vars(['project', 'mode'])==self.consts.modes.histo) $Poster.hide();
else {
var asPoster = {
type: 'poster',
formatted_time: '',
relative_time: 'Nouveau message'
};
getPost(asPoster).appendTo($Poster.show());
autosize($('#post'));
$('#submit').click(function(){
if($Poster.checkForm())
{
self.get(
'add_post',
function()
{
$('#name').val('');
$('#post').val('');
updateFeed(true);
},
{
project_id: self.vars(['project', 'id']),
name: $('#name').val(),
content: $('#post').val()
}
);
}
});
}
updateFeed(true);
}
function initSpotMessages(aoMessages, aoTracks) {
//Tile layers
var oMapBoxSat = L.tileLayer(self.tmp('tile_api'), {id: 'mapbox.satellite', minZoom: 0, maxZoom: 19}),
oOpenTopoMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {id: 'OpenTopoMap', minZoom: 2, maxZoom: 19}),
//oMapBoxStreet = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token='+self.vars('mapbox_key'), {id: 'mapbox.streets'}),
oIgnSpain = L.tileLayer(self.tmp('tile_api'), {id: 'ign.es', minZoom: 1, maxZoom: 20}),
oIgnFrance = L.tileLayer(self.tmp('tile_api'), {id: 'ign.fr', minZoom: 0, maxZoom: 18, tileSize: 256}),
oLinz = L.tileLayer(self.tmp('tile_api'), {id: 'linz', maxZoom: 17, continuousWorld: true, attribution: 'Sourced from LINZ. CC BY 4.0'});
//Map
var oMap = L.map(self.tmp('$Map')[0], {
layers: [oMapBoxSat],
attributionControl: false,
zoomControl: false
});
self.tmp('map', oMap);
//Tracks, colors & popup
var oTracks = L.geoJson(aoTracks, {
style: function(oTrack) {
return self.tmp(['track-type-styles', oTrack.properties.type]);
},
onEachFeature: function (feature, oLayer) {
var asProperties = feature.properties;
var $Tooltip = $('<div>', {'class':'track_tooltip'});
$('<p>', {'class':'name'}).addIcon('fa-track-'+asProperties.type, true).append(asProperties.name).appendTo($Tooltip);
if(asProperties.Name != asProperties.description) $('<p>', {'class':'description'}).text(asProperties.description).appendTo($Tooltip);
oLayer
.bindPopup($Tooltip[0])
.on('mouseover', function(e) {
var popup = e.target.getPopup();
popup.setLatLng(e.latlng).openOn(self.tmp('map'));
})
.on('mouseout', function(e) {
e.target.closePopup();
});
}
}).addTo(oMap);
//Centering map
if(self.vars(['project', 'mode'])==self.consts.modes.blog && aoMessages.length > 0)
{
//Zoom on last message
var oLastMsg = aoMessages[aoMessages.length-1];
oMap.setView(L.latLng(oLastMsg.latitude, oLastMsg.longitude), 15);
//Recenter map to be at the center of 70% (map_offset) of the page, 30% being used by posts
oMap.setOffsetView(self.tmp('map_offset'));
}
else oMap.fitBounds(oTracks.getBounds(), {paddingTopLeft: L.point(5, 42), paddingBottomRight: L.point(self.tmp('feed_width')+5, 5)});
//Controls
L.control.layers(
{
'Satellite': oMapBoxSat,
'Open Topo Map': oOpenTopoMap,
'IGN (France)': oIgnFrance,
'IGN (Espagne)': oIgnSpain,
'LINZ (Nouvelle-Zélande)': oLinz
},
null,
{position: 'topleft'}
).addTo(oMap);
//Building messages
$.each(aoMessages, function(iKey, oMsg){
//Marker
var oMarker = L.marker(L.latLng(oMsg.latitude, oMsg.longitude), {
id: oMsg.id_message,
riseOnHover: true,
icon: L.icon({
iconUrl: (iKey%2==0)?'images/footprint_alt.png':'images/footprint.png',
iconSize: [32, 37],
iconAnchor: [16, 37]
})
}).addTo(oMap);
//Marker events
/*oMarker.on({
mouseover: function(){
this.openPopup();
},
mouseout: function(){
},
click: function(oPoint){
self.tmp('map').setOffsetView(self.tmp('map_offset'), oPoint.latlng, 15);
}
});*/
//Tooltip
$Tooltip = $('<div>', {'class':'info-window'})
.append($('<h1>')
.append('Message '+oMsg.type+' #'+oMsg.id_message))
.append($('<p>', {'class':'time'})
.addIcon('fa-time')
.append(oMsg.formatted_time+(self.vars(['project', 'mode'])==self.consts.modes.blog?' ('+oMsg.relative_time+')':'')))
.append($('<p>', {'class':'coordinates'})
.addIcon('fa-message', false)
.append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
//Tooltip pictures
if(oMsg.pics) {
var $Pics = $('<div>', {'class':'pics'});
$.each(oMsg.pics, function(iKey, asPic){
$Pics.append($('<a>', {href: asPic.pic_path, 'data-lightbox': self.consts.title, 'data-title': asPic.formatted_time+' (heure française)'})
.append($('<img>', {'src': asPic.thumb_path})));
});
$Tooltip
.append($('<p>').addIcon('fa-picture').append('Photos'))
.append($Pics);
}
oMarker.bindPopup($Tooltip[0], {
maxWidth: 1000,
keepInView: true,
closeOnClick: true,
offset: new L.Point(0, -30)
});
});
//Legend
var oLegend = L.control({position: 'bottomleft'});
oLegend.onAdd = function(oMap) {return $('#legend').clone()[0];};
oLegend.addTo(oMap);
//Projects
var oProjects = L.control({position: 'bottomleft'});
oProjects.onAdd = function(oMap) {
var $Labels = $('<div>', {'class': 'leaflet-control-layers-base'});
$.each(self.vars('projects'), function(sCodeName, asProject){
var asRadioAttrs = {'type': 'radio', 'class': 'leaflet-control-layers-selector', 'name':'project', 'value': sCodeName};
if(asProject.id == self.vars(['project', 'id'])) asRadioAttrs.checked = 'checked';
var $Radio =$('<input>', asRadioAttrs).change(function(){
self.setHash(self.vars('page'), [$(this).val()]);
});
var $Label = $('<label>').append($('<div>')
.append($Radio)
.append($('<span>').text(' '+asProject.name))
);
$Labels.append($Label);
});
return $('<div>', {'class':'leaflet-control-layers leaflet-control leaflet-control-layers-expanded'}).append($('<section>').append($Labels))[0];
};
oProjects.addTo(oMap);
}
function getBoundsZoomLevel(bounds, mapDim) {
var WORLD_DIM = { height: 256, width: 256 };
var ZOOM_MAX = 21;
function latRad(lat) {
var sin = Math.sin(lat * Math.PI / 180);
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
}
function zoom(mapPx, worldPx, fraction) {
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
}
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI;
var lngDiff = ne.lng - sw.lng;
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);
return Math.min(latZoom, lngZoom, ZOOM_MAX);
}
L.Map.include({
setOffsetView: function (iOffsetRatioX, oCenter, iZoomLevel) {
var oCenter = (typeof oCenter == 'object')?$.extend({}, oCenter):this.getCenter();
iZoomLevel = iZoomLevel || this.getZoom();
var oBounds = this.getBounds();
var iOffsetX = (oBounds.getEast() - oBounds.getWest()) * iOffsetRatioX / ( 2 * Math.pow(2, iZoomLevel - this.getZoom()));
oCenter.lng = oCenter.lng - iOffsetX;
this.setView(oCenter, iZoomLevel);
}
});
function onFeedScroll(){
var $Box = $(this);
var $BoxContent = $Box.find('.simplebar-content');
if(($Box.scrollTop() + $(window).height()) / $BoxContent.height() >= 0.8) updateFeed();
}
function updateFeed(bFirstChunk)
{
bFirstChunk = bFirstChunk || false;
if(self.tmp('updatable')) {
if(!self.tmp('out-of-data') || bFirstChunk) {
var $Posts = $('#posts_list');
if(bFirstChunk===true) {
$Posts.empty();
self.tmp('news_chunk', 0);
}
self.tmp('updatable', false);
$('#loading').fadeIn('fast');
self.get('feed', function(asData) {
$('#loading').hide();
$.each(asData, function(iKey, asPost){
getPost(asPost).appendTo($Posts);
});
self.tmp('news_chunk', self.tmp('news_chunk') + 1);
self.tmp('out-of-data', Object.keys(asData).length != self.vars('chunk_size'));
self.tmp('updatable', true);
}, {
'project_id': self.vars(['project', 'id']),
'chunk': self.tmp('news_chunk')
});
}
}
else if(bFirstChunk) { //Delaying important data load
if(typeof oUpdateTimer != 'undefined') clearTimeout(oUpdateTimer);
oUpdateTimer = setTimeout(function(){updateFeed(true);}, 200);
}
}
function getPost(asPost) {
var $Post = $('<div>', {'class':'post '+asPost.type});
var sRelTime = (asPost.relative_time!='')?((self.vars(['project', 'mode'])==self.consts.modes.histo)?asPost.formatted_time.substr(0, 10):asPost.relative_time):'';
var sAbsTime = asPost.formatted_time;
var $Body = {};
switch(asPost.type)
{
case 'message':
$Body = $('<div>')
.append($('<p>').addIcon('fa-message', true).append(asPost.lat_dms+' '+asPost.lon_dms))
.append($('<p>').addIcon('fa-time', true).append(sAbsTime))
.append(
$('<img>', {'class':'staticmap', title: 'Click pour zoomer', src: getStaticMapUrl(asPost.latitude, asPost.longitude)})
.data('lat', asPost.latitude)
.data('lng', asPost.longitude)
.click(function(){
var $This = $(this);
var oCenter = L.latLng(parseFloat($This.data('lat')), parseFloat($This.data('lng')));
self.tmp('map').setOffsetView(self.tmp('map_offset'), oCenter, 13);
})
);
break;
case 'picture':
var $Image = $('<img>', {'src': asPost.thumb_path/*, 'style':'transform:rotate('+asPost.rotate+'deg);'*/});
$Body = $('<a>', {href: asPost.pic_path, 'data-lightbox': self.consts.title, 'data-title': 'Photo ajoutée le '+sAbsTime+' et prise le '+asPost.taken_on_formatted+' (heure française)'}).append($Image);
break;
case 'post':
$Body = $('<div>')
.append($('<p>', {'class':'message'}).text(asPost.content))
.append($('<p>', {'class':'signature'}).text('-- '+asPost.formatted_name));
break;
case 'poster':
$Body = $('<p>', {'class':'message'})
.append($('<textarea>', {id:'post', name:'post', placeholder:'Message', 'class':'autoExpand', rows:'1'}))
.append($('<input>', {type:'text', id:'name', name:'name', placeholder:'Nom'}))
.append($('<button>', {type:'button', id:'submit', name:'submit'}).addIcon('fa-send'));
break;
case 'loading':
$Body = $('<p>', {'class':'spinner'}).addIcon('fa-spin fa-spinner');
break;
}
$Post
.append($('<div>', {'class':'header'})
.append($('<span>', {'class':'index'}).addIcon('fa-'+asPost.type))
.append($('<span>', {'class':'time', 'title':sAbsTime}).text(sRelTime)))
.append($('<div>', {'class':'body'}).append($Body));
if(asPost.displayed_id) $Post.find('.index').append(' '+asPost.displayed_id);
return $Post;
}
function getStaticMapUrl(oCenterLat, oCenterLng) {
return '?'+$.param({
'a': 'tile',
'id': 'static',
'z': 13,
'x': oCenterLng,
'y': oCenterLat
});
}
</script>