Spot v2: Project Management
This commit is contained in:
385
masks/project.html
Executable file
385
masks/project.html
Executable file
@@ -0,0 +1,385 @@
|
||||
<div id="messages">
|
||||
<div id="map">
|
||||
<div class="loader fa fa-map"></div>
|
||||
</div>
|
||||
<div id="legend">
|
||||
<div class="line main">Trajet principal</div>
|
||||
<div class="line off-track">Variante</div>
|
||||
<div class="line hitchhiking">Hors rando</div>
|
||||
</div>
|
||||
<div id="feed">
|
||||
<div id="posts">
|
||||
<div id="poster"></div>
|
||||
<div id="posts_list"></div>
|
||||
<div id="loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="script/lightbox.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
self.onSamePageMove = function(asHash){
|
||||
self.tmp('map').remove();
|
||||
self.tmp('$Map').empty();
|
||||
self.pageInit(asHash);
|
||||
return false;
|
||||
};
|
||||
|
||||
oSpot.pageInit = function(asHash)
|
||||
{
|
||||
//Set active project
|
||||
if(asHash.items.length==0) {
|
||||
self.setHash(asHash.page, [self.vars('default_project_codename')]);
|
||||
return;
|
||||
}
|
||||
self.vars('project', self.vars(['projects', asHash.items[0]]));
|
||||
self.tmp('$Map', $('#map'));
|
||||
self.tmp('tracktype-colors', 'object');
|
||||
|
||||
//Assign Track Type Colors
|
||||
$('#legend').find('.line').each(function(iKey, oLegend){
|
||||
var $Legend = $(oLegend);
|
||||
var sTrackType = $Legend.attr('class').replace('line', '').trim();
|
||||
self.tmp(['tracktype-colors', sTrackType], $Legend.css('border-top-color'));
|
||||
});
|
||||
|
||||
//Add "Loading" Post
|
||||
var asLoading = {
|
||||
type: 'loading',
|
||||
formatted_time: '',
|
||||
relative_time: '',
|
||||
displayed_id: 'Chargement...'
|
||||
};
|
||||
getPost(asLoading).appendTo($('#loading'));
|
||||
|
||||
//Spot Messages
|
||||
$.when(
|
||||
self.get(
|
||||
'messages',
|
||||
function(){},
|
||||
{project_id: self.vars(['project', 'id'])},
|
||||
function(e){console.log(e);}
|
||||
),
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: self.consts.geo_folder+self.vars(['project', 'geofile']),
|
||||
mimeType: 'application/json'
|
||||
})
|
||||
).done(function(oMessages, aoTracks) {
|
||||
buildSpotMessages(oMessages[0]['data'], aoTracks[0]);
|
||||
});
|
||||
|
||||
//Posts
|
||||
updateFeed(true);
|
||||
initPosts();
|
||||
};
|
||||
|
||||
function buildSpotMessages(oMessages, aoTracks){
|
||||
|
||||
//Build Feed
|
||||
self.tmp('updatable', true);
|
||||
self.tmp('out-of-data', 'boolean');
|
||||
self.tmp('simple-bar', new SimpleBar($('#posts')[0]));
|
||||
self.tmp('simple-bar').getScrollElement().addEventListener('scroll', onFeedScroll);
|
||||
self.tmp('infowindow', 'boolean');
|
||||
self.tmp('feed_width', $('#feed').outerWidth(true));
|
||||
self.tmp('map_offset', -1 * self.tmp('feed_width') / $('body').outerWidth(true));
|
||||
self.tmp('map_padding', 0.05);
|
||||
self.tmp('tile_api', '?a=tile&id={id}&z={z}&x={x}&y={y}');
|
||||
|
||||
//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'});
|
||||
//oGoogleSatellite = L.tileLayer('https://mt.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {id: 'GoogleSatellite', minZoom: 1, maxZoom: 22});
|
||||
|
||||
//Map
|
||||
var oMap = L.map(self.tmp('$Map')[0], {
|
||||
//center: agCenter,
|
||||
//zoom: iZoom,
|
||||
layers: [oMapBoxSat],
|
||||
attributionControl: false,
|
||||
zoomControl: false
|
||||
});
|
||||
|
||||
//Tracks, colors & popup
|
||||
var oTracks = L.geoJson(aoTracks, {
|
||||
style: function(oTrack) {return {color: self.tmp(['tracktype-colors', oTrack.properties.type]), weight: 4, opacity: 1};}
|
||||
})
|
||||
.bindPopup(function(oLayer) {
|
||||
var asProperties = oLayer.feature.properties;
|
||||
var $Tooltip = $('<div>', {'class':'track_tooltip'});
|
||||
$('<p>', {'class':'name'}).text(asProperties.name).appendTo($Tooltip);
|
||||
if(asProperties.Name != asProperties.description) $('<p>', {'class':'description'}).text(asProperties.description).appendTo($Tooltip);
|
||||
return $Tooltip[0];
|
||||
})
|
||||
.addTo(oMap);
|
||||
|
||||
//Centering map
|
||||
if(self.vars(['project', 'mode'])==self.consts.modes.blog)
|
||||
{
|
||||
//Zoom on last message
|
||||
var oLastMsg = oMessages[oMessages.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,
|
||||
'IGN (France)': oIgnFrance,
|
||||
'IGN (Espagne)': oIgnSpain,
|
||||
'LINZ (Nouvelle-Zélande)': oLinz
|
||||
}, null, {position: 'topleft'}).addTo(oMap);
|
||||
|
||||
//Building messages
|
||||
$.each(oMessages, 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-clock-o').append(oMsg.formatted_time+' ('+oMsg.relative_time+')'))
|
||||
.append($('<p>', {'class':'coordinates'}).addIcon('fa-compass').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.path, 'data-lightbox': self.consts.title, 'data-title': asPic.formatted_time})
|
||||
.append($('<img>', {'src': asPic.thumb_path})));
|
||||
});
|
||||
$Tooltip
|
||||
.append($('<p>').addIcon('fa-image').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: 'bottomright'});
|
||||
oLegend.onAdd = function(oMap) {return L.DomUtil.get('legend');};
|
||||
oLegend.addTo(oMap);
|
||||
|
||||
self.tmp('map', oMap);
|
||||
}
|
||||
|
||||
function initPosts() {
|
||||
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'));
|
||||
|
||||
//$('#name').defaultVal('Nom...');
|
||||
//$('#post').defaultVal('Ton message...');
|
||||
$('#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()
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 = (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-compass', true).append('Latitude '+asPost.latitude+', Longitude '+asPost.longitude))
|
||||
.append($('<p>').addIcon('fa-clock-o', 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);
|
||||
})
|
||||
);
|
||||
sClass = 'compass';
|
||||
break;
|
||||
case 'picture':
|
||||
var $Image = $('<img>', {'src': asPost.thumb_path/*, 'style':'transform:rotate('+asPost.rotate+'deg);'*/});
|
||||
$Body = $('<a>', {href: asPost.path, 'data-lightbox': self.consts.title, 'data-title': sAbsTime}).append($Image);
|
||||
sClass = 'image';
|
||||
break;
|
||||
case 'post':
|
||||
$Body = $('<div>')
|
||||
.append($('<p>', {'class':'message'}).text(asPost.content))
|
||||
.append($('<p>', {'class':'signature'}).text('-- '+asPost.formatted_name));
|
||||
sClass = 'comment';
|
||||
break;
|
||||
case 'poster':
|
||||
$Body = $('<p>', {'class':'message'})
|
||||
.append($('<input>', {type:'text', id:'post', name:'post', placeholder:'Message'}))
|
||||
.append($('<input>', {type:'text', id:'name', name:'name', placeholder:'Nom'}))
|
||||
.append($('<button>', {type:'button', id:'submit', name:'submit'}).addIcon('fa-send'));
|
||||
sClass = 'comment';
|
||||
break;
|
||||
case 'loading':
|
||||
$Body = $('<p>', {'class':'spinner'}).addIcon('fa-spin fa-spinner');
|
||||
sClass = 'tasks';
|
||||
break;
|
||||
}
|
||||
$Post
|
||||
.append($('<div>', {'class':'header'})
|
||||
.append($('<span>', {'class':'index'}).addIcon('fa-'+sClass))
|
||||
.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) {
|
||||
var asParams = [
|
||||
'https://api.mapbox.com/v4/mapbox.satellite', //Domain
|
||||
'url-http%3A%2F%2Fspot.lutran.fr%2Fimages%2Ffootprint.png('+oCenterLng+','+oCenterLat+')', //Marker
|
||||
oCenterLng+','+oCenterLat+',13', //Center + zoom
|
||||
'400x300.png?access_token='+self.vars('mapbox_key') //Image size + access token
|
||||
];
|
||||
|
||||
return asParams.join('/');
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user