New settings panel & &subscription
This commit is contained in:
14
files/db/update_v8_to_v9.sql
Normal file
14
files/db/update_v8_to_v9.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE `users` (
|
||||
`id_user` int(10) UNSIGNED auto_increment,
|
||||
`name` VARCHAR(100),
|
||||
`email` VARCHAR(320),
|
||||
`language` VARCHAR(2),
|
||||
`active` BOOLEAN,
|
||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id_user`),
|
||||
UNIQUE KEY `uni_email` (`email`)
|
||||
);
|
||||
|
||||
ALTER TABLE posts ADD COLUMN id_user int(10) UNSIGNED AFTER id_project;
|
||||
ALTER TABLE posts ADD INDEX(`id_user`);
|
||||
ALTER TABLE posts ADD FOREIGN KEY (`id_user`) REFERENCES users(`id_user`);
|
||||
106
inc/email.php
Normal file
106
inc/email.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class Email extends PhpObject {
|
||||
|
||||
private $sServName;
|
||||
private $sTemplate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Translator[]
|
||||
*/
|
||||
private $asTranslators;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Mask[]
|
||||
*/
|
||||
private $asTemplates;
|
||||
|
||||
private $asDests;
|
||||
|
||||
public function __construct($sServName, $sTemplate='') {
|
||||
$this->sServName = $sServName;
|
||||
$this->setTemplate($sTemplate);
|
||||
$this->asDests = array();
|
||||
}
|
||||
|
||||
public function setTemplate($sTemplate) {
|
||||
$this->sTemplate = $sTemplate;
|
||||
$this->asTranslators = array();
|
||||
$this->asTemplates = array();
|
||||
}
|
||||
|
||||
private function getTemplate($sLanguage) {
|
||||
if(!array_key_exists($sLanguage, $this->asTemplates)) {
|
||||
$this->asTranslators[$sLanguage] = new Translator($sLanguage);
|
||||
$this->buildTemplate($sLanguage);
|
||||
}
|
||||
|
||||
return array('subject'=>$this->asTranslators[$sLanguage]->getTranslation('conf_subject'), 'email'=>$this->asTemplates[$sLanguage]);
|
||||
}
|
||||
|
||||
private function buildTemplate($sLanguage) {
|
||||
$oTemplate = new Mask($this->sTemplate, $this->asTranslators[$sLanguage]);
|
||||
|
||||
switch($this->sTemplate) {
|
||||
case 'confirmation':
|
||||
break;
|
||||
case 'update':
|
||||
break;
|
||||
}
|
||||
|
||||
$this->asTemplates[$sLanguage] = $oTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Target User Info
|
||||
* @param array $asDests Contains: id_user, name, email, language, active
|
||||
*/
|
||||
public function setDestInfo($asDests) {
|
||||
if(array_key_exists('email', $asDests)) $asDests = array($asDests);
|
||||
$this->asDests = $asDests;
|
||||
}
|
||||
|
||||
public function send() {
|
||||
$sEOL = "\r\n";
|
||||
foreach($this->asDests as $asDest) {
|
||||
//Message
|
||||
$asTemplate = $this->getTemplate($asDest['language']);
|
||||
$oEmail = $asTemplate['email'];
|
||||
|
||||
//Unsubscribe Link
|
||||
$sUnsubLink = $this->sServName.'?a=unsubscribe_email&id='.$asDest['id_user'];
|
||||
$oEmail->setTag('unsubscribe_link', $sUnsubLink);
|
||||
|
||||
//Email Content
|
||||
$sHtmlMessage = $oEmail->getMask();
|
||||
$sPlainMessage = strip_tags(str_replace('<br />', "\n", $sHtmlMessage));
|
||||
|
||||
//Email
|
||||
$iBoundary = uniqid("HTMLEMAIL");
|
||||
$sHeaders =
|
||||
'From: Spotty <spot@lutran.fr>'.$sEOL.
|
||||
'Reply-To: Spotty <spot@lutran.fr>'.$sEOL.
|
||||
'List-Unsubscribe: <mailto:unsubscribe@'.parse_url($this->sServName)['host'].'?subject=unsubscribe>, <'.$sUnsubLink.'>'.$sEOL.
|
||||
'List-Unsubscribe-Post: List-Unsubscribe=One-Click'.$sEOL.
|
||||
'MIME-Version: 1.0'.$sEOL.
|
||||
'Content-Type: multipart/alternative; boundary="'.$iBoundary.'"'.$sEOL;
|
||||
|
||||
$sBody =
|
||||
'--'.$iBoundary.$sEOL. //Plain Message
|
||||
'Content-Type: text/plain; charset=UTF-8'.$sEOL.
|
||||
'Content-Transfer-Encoding: base64'.$sEOL.
|
||||
chunk_split(base64_encode($sPlainMessage)).$sEOL.
|
||||
|
||||
'--'.$iBoundary.$sEOL. //HTML Message
|
||||
'Content-Type: text/html; charset=UTF-8'.$sEOL.
|
||||
'Content-Transfer-Encoding: base64'.$sEOL.
|
||||
chunk_split(base64_encode($sHtmlMessage)).$sEOL.
|
||||
'--'.$iBoundary.'--';
|
||||
|
||||
//Send
|
||||
if(!mail($asDest['email'], $asTemplate['subject'], $sBody, $sHeaders)) $this->addError('Could not send '.$this->sTemplate.' email to '.$asDest['email']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ class Feed extends PhpObject {
|
||||
}
|
||||
|
||||
public function checkUpdateFeed($sProjectMode) {
|
||||
//Feed updated once a day in Blog Mode
|
||||
//Feed updated once every hour in Blog Mode
|
||||
if($sProjectMode == Project::MODE_BLOG && date('Y-m-d-H', $this->iLastUpdate) != date('Y-m-d-H')) $this->updateFeed();
|
||||
}
|
||||
|
||||
|
||||
84
inc/spot.php
84
inc/spot.php
@@ -21,6 +21,8 @@ class Spot extends Main
|
||||
|
||||
const FEED_CHUNK_SIZE = 15;
|
||||
|
||||
const DEFAULT_LANG = 'en';
|
||||
|
||||
/**
|
||||
* Active Project
|
||||
* @var Project
|
||||
@@ -33,18 +35,27 @@ class Spot extends Main
|
||||
*/
|
||||
private $oMedia;
|
||||
|
||||
/**
|
||||
* User
|
||||
* @var User
|
||||
*/
|
||||
private $oUser;
|
||||
|
||||
public function __construct($oClassManagement, $sProcessPage, $sTimezone)
|
||||
{
|
||||
$asClasses = array(
|
||||
array('name'=>'feed', 'project'=>true),
|
||||
array('name'=>'project', 'project'=>true),
|
||||
array('name'=>'media', 'project'=>true),
|
||||
array('name'=>'converter', 'project'=>true)
|
||||
array('name'=>'converter', 'project'=>true),
|
||||
array('name'=>'user', 'project'=>true)
|
||||
);
|
||||
parent::__construct($oClassManagement, $sProcessPage, $asClasses, true, __FILE__, $sTimezone);
|
||||
|
||||
$this->oUser = new User($this->oDb);
|
||||
|
||||
$this->oClassManagement->incClass('translator');
|
||||
$this->oLang = new Translator('', 'en');
|
||||
$this->oLang = new Translator($this->oUser->getLang(), self::DEFAULT_LANG);
|
||||
|
||||
$this->oProject = new Project($this->oDb);
|
||||
$this->oMedia = new Media($this->oDb, $this->oProject);
|
||||
@@ -66,8 +77,9 @@ class Spot extends Main
|
||||
Feed::FEED_TABLE => array('ref_feed_id', Db::getId(Feed::SPOT_TABLE), Db::getId(Project::PROJ_TABLE), 'name', 'description', 'status', 'last_update'),
|
||||
Feed::SPOT_TABLE => array('ref_spot_id', 'name', 'model'),
|
||||
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to', 'timezone'),
|
||||
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), 'name', 'content', 'site_time'),
|
||||
Media::MEDIA_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'type', 'taken_on', 'posted_on', 'rotate', 'comment')
|
||||
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), Db::getId(User::USER_TABLE), 'name', 'content', 'site_time'),
|
||||
Media::MEDIA_TABLE => array(Db::getId(Project::PROJ_TABLE), 'filename', 'type', 'taken_on', 'posted_on', 'rotate', 'comment'),
|
||||
User::USER_TABLE => array('name', 'email', 'language', 'active')
|
||||
),
|
||||
'types' => array
|
||||
(
|
||||
@@ -95,7 +107,10 @@ class Spot extends Main
|
||||
'taken_on' => "TIMESTAMP DEFAULT 0",
|
||||
'posted_on' => "TIMESTAMP DEFAULT 0",
|
||||
'rotate' => "SMALLINT",
|
||||
'comment' => "LONGTEXT"
|
||||
'comment' => "LONGTEXT",
|
||||
'email' => "VARCHAR(320)",
|
||||
'language' => "VARCHAR(2)",
|
||||
'active' => "BOOLEAN"
|
||||
),
|
||||
'constraints' => array
|
||||
(
|
||||
@@ -103,7 +118,8 @@ class Spot extends Main
|
||||
Feed::FEED_TABLE => array("UNIQUE KEY `uni_ref_feed_id` (`ref_feed_id`)", "INDEX(`ref_feed_id`)"),
|
||||
Feed::SPOT_TABLE => array("UNIQUE KEY `uni_ref_spot_id` (`ref_spot_id`)", "INDEX(`ref_spot_id`)"),
|
||||
Project::PROJ_TABLE => "UNIQUE KEY `uni_proj_name` (`codename`)",
|
||||
Media::MEDIA_TABLE => "UNIQUE KEY `uni_file_name` (`filename`)"
|
||||
Media::MEDIA_TABLE => "UNIQUE KEY `uni_file_name` (`filename`)",
|
||||
User::USER_TABLE => "UNIQUE KEY `uni_email` (`email`)"
|
||||
),
|
||||
'cascading_delete' => array
|
||||
(
|
||||
@@ -119,7 +135,8 @@ class Spot extends Main
|
||||
'vars' => array(
|
||||
'chunk_size' => self::FEED_CHUNK_SIZE,
|
||||
'default_project_codename' => $this->oProject->getProjectCodeName(),
|
||||
'projects' => $this->oProject->getProjects()
|
||||
'projects' => $this->oProject->getProjects(),
|
||||
'user' => $this->oUser->getUserInfo()
|
||||
),
|
||||
'consts' => array(
|
||||
'geo_server' => Settings::GEO_SERVER,
|
||||
@@ -155,7 +172,7 @@ class Spot extends Main
|
||||
public function getMarkers()
|
||||
{
|
||||
$asMessages = $this->getSpotMessages();
|
||||
$bSuccess = !empty($asMessages);
|
||||
$bSuccess = !empty($this->getMedias('posted_on') + $asMessages + $this->getPosts());
|
||||
$sDesc = $bSuccess?'':self::NO_DATA;
|
||||
|
||||
//Add medias
|
||||
@@ -183,6 +200,35 @@ class Spot extends Main
|
||||
return self::getJsonResult($bSuccess, $sDesc, $asMessages);
|
||||
}
|
||||
|
||||
public function subscribe($sEmail) {
|
||||
$asResult = $this->oUser->addUser($sEmail, $this->oLang->getLanguage());
|
||||
|
||||
//Send Confirmation Email
|
||||
if($asResult['result']) {
|
||||
$this->oClassManagement->incClass('email', true);
|
||||
$oConfEmail = new Email($this->asContext['serv_name'],'email_confirmation');
|
||||
$oConfEmail->setDestInfo($this->oUser->getUserInfo());
|
||||
$oConfEmail->send();
|
||||
}
|
||||
|
||||
return self::getJsonResult($asResult['result'], $asResult['desc'], $asResult['data']);
|
||||
}
|
||||
|
||||
public function unsubscribe() {
|
||||
$asResult = $this->oUser->removeUser();
|
||||
return self::getJsonResult($asResult['result'], $asResult['desc'], $asResult['data']);
|
||||
}
|
||||
|
||||
public function unsubscribeFromEmail($iUserId) {
|
||||
$this->oUser->setUserId($iUserId);
|
||||
$this->oLang->setLanguage($this->oUser->getLang(), self::DEFAULT_LANG);
|
||||
$asResult = $this->oUser->removeUser();
|
||||
|
||||
$sDesc = $asResult['desc'];
|
||||
if($sDesc=='') $sDesc = $this->oLang->getTranslation('nl_unsubscribed');
|
||||
return $sDesc;
|
||||
}
|
||||
|
||||
private function getSpotMessages()
|
||||
{
|
||||
$asMessages = array();
|
||||
@@ -192,20 +238,22 @@ class Spot extends Main
|
||||
foreach($asFeeds as $iFeedId) {
|
||||
$oFeed = new Feed($this->oDb, $iFeedId);
|
||||
$asMessages = $oFeed->getMessages($this->oProject->getActivePeriod());
|
||||
foreach($asMessages as $iIndex=>&$asMessage)
|
||||
foreach($asMessages as &$asMessage)
|
||||
{
|
||||
$asMessage['latitude'] = floatval($asMessage['latitude']);
|
||||
$asMessage['longitude'] = floatval($asMessage['longitude']);
|
||||
$asMessage['lat_dms'] = self::decToDms($asMessage['latitude'], 'lat');
|
||||
$asMessage['lon_dms'] = self::decToDms($asMessage['longitude'], 'lon');
|
||||
$asMessage['displayed_id'] = $iIndex + 1;
|
||||
|
||||
$this->addTimeStamp($asMessage, $asMessage['unix_time']);
|
||||
}
|
||||
}
|
||||
usort($asMessages, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
||||
|
||||
return $asMessages;
|
||||
$asSortedMessages = array_values($asMessages);
|
||||
foreach($asSortedMessages as $iIndex=>&$asSortedMessage) $asSortedMessage['displayed_id'] = $iIndex + 1;
|
||||
|
||||
return $asSortedMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,20 +267,23 @@ class Spot extends Main
|
||||
{
|
||||
$asMedias = $this->oMedia->getMediasInfo();
|
||||
$asValidMedias = array();
|
||||
foreach($asMedias as $iIndex=>$asMedia) {
|
||||
foreach($asMedias as $asMedia) {
|
||||
$sTimeRef = $asMedia[$sTimeRefField];
|
||||
if($sTimeRef >= $this->oProject->getActivePeriod('from') && $sTimeRef <= $this->oProject->getActivePeriod('to')) {
|
||||
$asMedia['taken_on_formatted'] = $this->getTimeFormat(strtotime($asMedia['taken_on']));
|
||||
$asMedia['posted_on_formatted'] = $this->getTimeFormat(strtotime($asMedia['posted_on']));
|
||||
$asMedia['displayed_id'] = $iIndex + 1;
|
||||
|
||||
$this->addTimeStamp($asMedia, strtotime($sTimeRef));
|
||||
$asValidMedias[] = $asMedia;
|
||||
}
|
||||
}
|
||||
|
||||
usort($asValidMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
||||
|
||||
return $asValidMedias;
|
||||
$asSortedMedias = array_values($asValidMedias);
|
||||
foreach($asSortedMedias as $iIndex=>&$asSortedMedia) $asSortedMedia['displayed_id'] = $iIndex + 1;
|
||||
|
||||
return $asSortedMedias;
|
||||
}
|
||||
|
||||
private function getPosts()
|
||||
@@ -251,6 +302,7 @@ class Spot extends Main
|
||||
foreach($asPosts as &$asPost) {
|
||||
$iUnixTimeStamp = strtotime($asPost['site_time']); //assumes site timezone (Settings::TIMEZONE)
|
||||
$asPost['formatted_name'] = Toolbox::mb_ucwords($asPost['name']);
|
||||
unset($asPost[Db::getId(User::USER_TABLE)]);
|
||||
|
||||
$this->addTimeStamp($asPost, $iUnixTimeStamp);
|
||||
}
|
||||
@@ -315,11 +367,15 @@ class Spot extends Main
|
||||
{
|
||||
$asData = array(
|
||||
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
|
||||
Db::getId(User::USER_TABLE) => $this->oUser->getUserId(),
|
||||
'name' => mb_strtolower(trim($sName)),
|
||||
'content' => trim($sPost),
|
||||
'site_time' => date(Db::TIMESTAMP_FORMAT) //site time (Settings::TIMEZONE)
|
||||
);
|
||||
$iPostId = $this->oDb->insertRow(self::POST_TABLE, $asData);
|
||||
|
||||
$this->oUser->updateNickname($sName);
|
||||
|
||||
return self::getJsonResult(($iPostId > 0), '');
|
||||
}
|
||||
|
||||
|
||||
108
inc/user.php
Normal file
108
inc/user.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
class User extends PhpObject {
|
||||
|
||||
//DB Tables
|
||||
const USER_TABLE = 'users';
|
||||
|
||||
//Cookie
|
||||
const COOKIE_ID_USER = 'subscriber';
|
||||
|
||||
/**
|
||||
* Database Handle
|
||||
* @var Db
|
||||
*/
|
||||
private $oDb;
|
||||
|
||||
//User Info
|
||||
private $iUserId;
|
||||
private $asUserInfo;
|
||||
|
||||
public function __construct(Db &$oDb) {
|
||||
parent::__construct(__CLASS__, Settings::DEBUG);
|
||||
$this->oDb = &$oDb;
|
||||
$this->iUserId = 0;
|
||||
$this->asUserInfo = array('name'=>'', 'email'=>'', 'language'=>'', 'active'=>0);
|
||||
$this->checkUserCookie();
|
||||
}
|
||||
|
||||
public function getLang() {
|
||||
return $this->asUserInfo['language'];
|
||||
}
|
||||
|
||||
public function addUser($sEmail, $sLang) {
|
||||
$bSuccess = false;
|
||||
$sDesc = '';
|
||||
$sEmail = trim($sEmail);
|
||||
|
||||
//Check Email availability
|
||||
$iUserId = $this->oDb->selectValue(self::USER_TABLE, Db::getId(self::USER_TABLE), array('email'=>$sEmail, 'active'=>true));
|
||||
|
||||
if($iUserId > 0) $sDesc = 'lang:nl_email_exists';
|
||||
else {
|
||||
//Add/Reactivate user
|
||||
$iUserId = $this->oDb->insertUpdateRow(self::USER_TABLE, array('email'=>$sEmail, 'language'=>$sLang, 'active'=>true), array('email'));
|
||||
if($iUserId==0) $sDesc = 'lang:error_commit_db';
|
||||
else $bSuccess = true;
|
||||
}
|
||||
|
||||
//Set Cookie (valid 1 year)
|
||||
if($iUserId > 0) {
|
||||
$this->setUserId($iUserId);
|
||||
$this->updateCookie();
|
||||
}
|
||||
|
||||
return Spot::getResult($bSuccess, $sDesc, $this->getUserInfo());
|
||||
}
|
||||
|
||||
public function removeUser() {
|
||||
$bSuccess = false;
|
||||
$sDesc = '';
|
||||
|
||||
if($this->iUserId > 0) {
|
||||
$iUserId = $this->oDb->updateRow(self::USER_TABLE, $this->iUserId, array('active'=>false));
|
||||
if($iUserId==0) $sDesc = 'lang:error_commit_db';
|
||||
else $bSuccess = true;
|
||||
}
|
||||
else $sDesc = 'lang:nl_unknown_email';
|
||||
|
||||
return Spot::getResult($bSuccess, $sDesc);
|
||||
}
|
||||
|
||||
public function updateNickname($sNickname) {
|
||||
if($this->iUserId > 0 && $sNickname!='') $this->oDb->updateRow(self::USER_TABLE, $this->iUserId, array('name'=>$sNickname));
|
||||
}
|
||||
|
||||
private function checkUserCookie() {
|
||||
if(isset($_COOKIE[self::COOKIE_ID_USER])){
|
||||
$this->setUserId($_COOKIE[self::COOKIE_ID_USER]);
|
||||
|
||||
//Extend cookie life
|
||||
if($this->iUserId > 0) $this->updateCookie();
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserId() {
|
||||
return $this->iUserId;
|
||||
}
|
||||
|
||||
public function getUserInfo() {
|
||||
$asUserInfo = $this->asUserInfo;
|
||||
$asUserInfo[Db::getId(self::USER_TABLE)] = $this->iUserId;
|
||||
return $asUserInfo;
|
||||
}
|
||||
|
||||
public function setUserId($iUserId) {
|
||||
$this->iUserId = 0;
|
||||
|
||||
$asUser = $this->oDb->selectRow(self::USER_TABLE, array(Db::getId(self::USER_TABLE)=>$iUserId, 'active'=>true), array_keys($this->asUserInfo));
|
||||
if(!empty($asUser)) {
|
||||
$this->iUserId = $iUserId;
|
||||
$this->asUserInfo = $asUser;
|
||||
}
|
||||
}
|
||||
|
||||
private function updateCookie() {
|
||||
setcookie(self::COOKIE_ID_USER, $this->iUserId, time() + 60 * 60 * 24 * 365);
|
||||
}
|
||||
}
|
||||
10
index.php
10
index.php
@@ -22,6 +22,7 @@ $sField = isset($_REQUEST['field'])?$_REQUEST['field']:'';
|
||||
$oValue = isset($_REQUEST['value'])?$_REQUEST['value']:'';
|
||||
$iId = isset($_REQUEST['id'])?$_REQUEST['id']:0;
|
||||
$sType = isset($_REQUEST['type'])?$_REQUEST['type']:'';
|
||||
$sEmail = isset($_REQUEST['email'])?$_REQUEST['email']:'';
|
||||
|
||||
//Initiate class
|
||||
$oSpot = new Spot($oClassManagement, __FILE__, $sTimezone);
|
||||
@@ -65,6 +66,15 @@ if($sAction!='')
|
||||
case 'build_geojson':
|
||||
$sResult = $oSpot->convertGpxToGeojson($sName);
|
||||
break;
|
||||
case 'subscribe':
|
||||
$sResult = $oSpot->subscribe($sEmail);
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
$sResult = $oSpot->unsubscribe();
|
||||
break;
|
||||
case 'unsubscribe_email':
|
||||
$sResult = $oSpot->unsubscribeFromEmail($iId);
|
||||
break;
|
||||
default:
|
||||
$sResult = Main::getJsonResult(false, Main::NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ admin_save_success = Saved
|
||||
track_main = Main track
|
||||
track_offtrack = Off-track
|
||||
track_hitchhiking = Hitchhiking
|
||||
track_download = Download GPX Track
|
||||
|
||||
upload_title = Picture & Video Uploads
|
||||
upload_wrong_mode = Project "$0" is not in blog mode. No upload allowed
|
||||
@@ -20,6 +21,7 @@ post_new_message = New message
|
||||
and = and
|
||||
counter = #$0
|
||||
|
||||
maps = Base Maps
|
||||
map_satellite = Satellite
|
||||
map_otm = Open Topo Map
|
||||
map_ign_france = IGN (France)
|
||||
@@ -38,11 +40,13 @@ click_zoom = Click to zoom
|
||||
media_count = Media $0 / $1
|
||||
media_no_id = Missing Media ID in request
|
||||
media_comment_update= Comment of media "$0" updated
|
||||
see_on_google = See on Google Maps
|
||||
|
||||
city_time = $0 Time
|
||||
|
||||
project_id = Project ID
|
||||
project = Project
|
||||
projects = Projects
|
||||
mode = Mode
|
||||
code_name = Code name
|
||||
start = Start
|
||||
@@ -62,3 +66,25 @@ time_zone = Time zone
|
||||
unit_day = day
|
||||
unit_days = days
|
||||
unit_hour = h
|
||||
|
||||
newsletter = Keep in touch!
|
||||
nl_email_placeholder= my@email.com
|
||||
nl_subscribed_desc = You're all set. We'll send you updates as soon as we get them
|
||||
nl_unsubscribed_desc= Write down your email address and we'll send you François' position as soon as we get it :)
|
||||
nl_email_exists = This email is already subscribed
|
||||
nl_subscribe = Subscribe
|
||||
nl_subscribed = Thanks! You'll receive a confirmation email shortly
|
||||
nl_unsubscribe = Unsubscribe
|
||||
nl_unsubscribed = Done. No more junk mail from us
|
||||
nl_unknown_email = Unknown email address
|
||||
|
||||
conf_subject = Successful Registration
|
||||
conf_preheader = Thanks for keeping in touch!
|
||||
conf_thanks_sub = You're all set!
|
||||
conf_body_para_1 = Thank you for checking in on my wanderings :). I'll make sure to keep you posted on my progress along the trail.
|
||||
conf_body_para_2 = I usually check-in once a day, plus sometimes on special events, like successful peak ascents. I am using a GPS-based device (PLB) which does not require phone reception to work. Thus the messages should be pretty frequent, but, being awestruck by the beauty of nature, I could also just forget to send a signal once in a while. So do not worry if you don't receive anything for a couple of days.
|
||||
conf_body_para_3 = If I've posted some pictures recently, you should also get them in this email.
|
||||
conf_body_conclusion= Happy Trails!
|
||||
conf_signature = --François
|
||||
conf_unsubscribe = PS: Changed your mind?
|
||||
conf_unsubscribe_btn= Unsubscribe
|
||||
@@ -8,6 +8,7 @@ admin_save_success = Sauvegardé
|
||||
track_main = Trajet principal
|
||||
track_offtrack = Variante
|
||||
track_hitchhiking = Hors rando
|
||||
track_download = Télécharger la trace GPX
|
||||
|
||||
upload_title = Uploader photos & vidéos
|
||||
upload_wrong_mode = Le projet "$0" n'est pas en mode blog. Aucun upload possible
|
||||
@@ -20,6 +21,7 @@ post_new_message = Nouveau message
|
||||
and = et
|
||||
counter = N° $0
|
||||
|
||||
maps = Fonds de carte
|
||||
map_satellite = Satellite
|
||||
map_otm = Open Topo Map
|
||||
map_ign_france = IGN (France)
|
||||
@@ -38,11 +40,13 @@ click_zoom = Click pour zoomer
|
||||
media_count = Média $0 sur $1
|
||||
media_no_id = ID du média manquant
|
||||
media_comment_update= Commentaire du media "$0" mis-à-jour
|
||||
see_on_google = Voir la position sur Google Maps
|
||||
|
||||
city_time = heure de $0
|
||||
|
||||
project_id = ID projet
|
||||
project = Projet
|
||||
projects = Projets
|
||||
mode = Mode
|
||||
code_name = Nom de code
|
||||
start = Départ
|
||||
@@ -62,3 +66,25 @@ time_zone = Fuseau horaire
|
||||
unit_day = jour
|
||||
unit_days = jours
|
||||
unit_hour = h
|
||||
|
||||
newsletter = Rester en contact
|
||||
nl_email_placeholder= mon@email.com
|
||||
nl_subscribed_desc = C'est tout bon. On t'enverra les nouvelles posititions dès qu'on les reçoit
|
||||
nl_unsubscribed_desc= Ajoute ton adresse email et on t'enverra la nouvelle position de François dès qu'on la reçoit :)
|
||||
nl_email_exists = Cette adresse email est déjà enregistrée
|
||||
nl_subscribe = S'abonner
|
||||
nl_subscribed = Merci ! Tu vas recevoir un email de confirmation très bientôt
|
||||
nl_unsubscribe = Se désinscrire
|
||||
nl_unsubscribed = C'est fait. Fini le spam!
|
||||
nl_unknown_email = Adresse email inconnue
|
||||
|
||||
conf_subject = Confirmation
|
||||
conf_preheader = Merci de rester en contact !
|
||||
conf_thanks_sub = C'est tout bon !
|
||||
conf_body_para_1 = C'est gentil de venir voir où j'en suis. Promis, je vous tiendrais au courant de mon avancée.
|
||||
conf_body_para_2 = En général, j'envoie un message une fois par jour. Lorsque je passe à des endroits sympas, j'en envoie un supplémentaire (ascension de sommets, ce genre de choses). J'utilise une balise GPS pour envoyer le signal, je n'ai donc pas besoin de réseau téléphonique pour que cela fonctionne. Cependant, il peut m'arriver d'appuyer sur le bouton. Donc pas de raison de s'inquiéter si vous ne recevez pas de messages pendant une journée ou deux.
|
||||
conf_body_para_3 = If I've posted some pictures recently, you should also get them in this email.
|
||||
conf_body_conclusion= A bientôt sur les chemins !
|
||||
conf_signature = --François
|
||||
conf_unsubscribe = PS: Trop d'emails ?
|
||||
conf_unsubscribe_btn= Se désinscrire
|
||||
@@ -48,7 +48,7 @@
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="feedback"></div>
|
||||
<div id="feedback" class="feedback"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
oSpot.pageInit = function(asHash) {
|
||||
|
||||
33
masks/email_confirmation.html
Normal file
33
masks/email_confirmation.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
||||
<title>[#]lang:conf_subject[#]</title>
|
||||
</head>
|
||||
<body>
|
||||
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:conf_preheader[#]</span>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
||||
<tr>
|
||||
<td width="20%"><img src="https://spot.lutran.fr/images/icons/mstile-144x144.png" width="90%" border="0" alt="logo" /></td>
|
||||
<td><h1>[#]lang:conf_thanks_sub[#]</h1></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p align="justify">[#]lang:conf_body_para_1[#]</p>
|
||||
<p align="justify">[#]lang:conf_body_para_2[#]</p>
|
||||
<p align="justify">[#]lang:conf_body_para_3[#]</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p>[#]lang:conf_body_conclusion[#]<br />[#]lang:conf_signature[#]</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p>[#]lang:conf_unsubscribe[#] <a href="[#]unsubscribe_link[#]" target="_blank">[#]lang:conf_unsubscribe_btn[#]</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,6 +3,24 @@
|
||||
<div class="loader fa fa-fw fa-map flicker" id="map_loading"></div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<div id="settings">
|
||||
<div id="settings-sections">
|
||||
<div class="settings-section">
|
||||
<h1><i class="fa fa-fw push fa-map"></i>[#]lang:maps[#]</h1>
|
||||
<div id="layers"></div>
|
||||
</div>
|
||||
<div class="settings-section newsletter">
|
||||
<h1><i class="fa fa-fw push fa-newsletter"></i>[#]lang:newsletter[#]</h1>
|
||||
<input type="email" name="email" id="email" placeholder="[#]lang:nl_email_placeholder[#]" /><button id="nl_btn"><span><i class="fa"></i></span></button>
|
||||
<div id="settings-feedback" class="feedback"></div>
|
||||
<div id="nl_desc"></div>
|
||||
</div>
|
||||
<div class="settings-section">
|
||||
<h1><i class="fa fa-fw push fa-project"></i>[#]lang:projects[#]</h1>
|
||||
<div id="settings-projects"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="feed">
|
||||
<div id="posts">
|
||||
<div id="poster"></div>
|
||||
@@ -11,6 +29,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="elems">
|
||||
<div id="settings-button"><i class="fa fa-menu fa-fw"></i></div>
|
||||
<div id="post-button"><i class="fa fa-fw"></i></div>
|
||||
<div id="legend" class="leaflet-control-layers leaflet-control leaflet-control-layers-expanded">
|
||||
<div class="track"><span class="line main"></span><span class="desc">[#]lang:track_main[#]</span></div>
|
||||
@@ -40,7 +59,6 @@ oSpot.pageInit = function(asHash) {
|
||||
};
|
||||
|
||||
oSpot.onResize = function() {
|
||||
self.tmp('mobile', $('#mobile').is(':visible'));
|
||||
self.tmp('feed_width', self.tmp('with_feed')?$('#feed').outerWidth(true):0);
|
||||
self.tmp('map_offset', -1 * self.tmp('feed_width') / $('body').outerWidth(true));
|
||||
|
||||
@@ -64,7 +82,7 @@ oSpot.onKeydown = function(oEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFeedPanel(bShow, bAutoPan) {
|
||||
function toggleFeedPanel(bShow, sMapAction) {
|
||||
var $Container = $('#projects');
|
||||
if(typeof bShow === 'undefined') $Container.toggleClass('with-feed');
|
||||
else $Container.toggleClass('with-feed', bShow);
|
||||
@@ -72,17 +90,36 @@ function toggleFeedPanel(bShow, bAutoPan) {
|
||||
oSpot.tmp('with_feed', $Container.hasClass('with-feed'));
|
||||
oSpot.onResize();
|
||||
|
||||
if(typeof bAutoPan === 'undefined') bAutoPan = true;
|
||||
if(bAutoPan && typeof oSpot.tmp('map') != 'undefined') {
|
||||
oSpot.tmp('map').panBy([(oSpot.tmp('with_feed')?1:-1)*$('#feed').outerWidth(true)/2, 0], {
|
||||
duration: 0.5
|
||||
});
|
||||
if(isMobile()) $('#settings-button').toggle(!oSpot.tmp('with_feed'));
|
||||
|
||||
sMapAction = sMapAction || 'panTo';
|
||||
switch(sMapAction) {
|
||||
case 'none': break;
|
||||
case 'panTo': oSpot.tmp('map').panBy([(oSpot.tmp('with_feed')?1:-1)*$('#feed').outerWidth(true)/2, 0], {duration: 0.5}); break;
|
||||
case 'panToInstant': oSpot.tmp('map').panBy([(oSpot.tmp('with_feed')?1:-1)*$('#feed').outerWidth(true)/2, 0]); break;
|
||||
case 'fitBounds': oSpot.tmp('map').fitBounds(self.tmp('track').getBounds(), {paddingTopLeft: L.point(5, self.tmp('marker_size').height + 5), paddingBottomRight: L.point(self.tmp('feed_width') + 5, 5)}); break;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSettingsPanel(bShow) {
|
||||
var $Container = $('#projects');
|
||||
if(typeof bShow === 'undefined') $Container.toggleClass('with-settings');
|
||||
else $Container.toggleClass('with-settings', bShow);
|
||||
|
||||
var bWithSettings = isSettingsPanelOpen();
|
||||
oSpot.onResize();
|
||||
|
||||
if(isMobile()) $('#post-button').toggle(!bWithSettings);
|
||||
|
||||
oSpot.tmp('map').panBy([(bWithSettings?-1:1)*$('#settings').outerWidth(true)/2, 0], {duration: 0.5});
|
||||
}
|
||||
|
||||
function isSettingsPanelOpen() {
|
||||
return $('#projects').hasClass('with-settings');
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
self.tmp('mobile', $('#mobile').is(':visible'));
|
||||
return self.tmp('mobile');
|
||||
return $('#mobile').is(':visible');
|
||||
}
|
||||
|
||||
function initPage(asHash) {
|
||||
@@ -97,8 +134,7 @@ function initPage(asHash) {
|
||||
self.tmp('trail-markers', 'object');
|
||||
self.tmp('marker_size', {width: 32, height: 32});
|
||||
|
||||
toggleFeedPanel(!isMobile(), false);
|
||||
oSpot.onResize();
|
||||
toggleFeedPanel(false, 'none');
|
||||
|
||||
//Lightbox options
|
||||
lightbox.option({
|
||||
@@ -119,6 +155,7 @@ function initPage(asHash) {
|
||||
self.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
|
||||
self.tmp('simple-bar', new SimpleBar($('#posts')[0]));
|
||||
self.tmp('simple-bar').getScrollElement().addEventListener('scroll', onFeedScroll);
|
||||
@@ -131,6 +168,12 @@ function initPage(asHash) {
|
||||
if(aiDelta.x > self.tmp('feed_width')/3 && aiDelta.x > Math.abs(aiDelta.y)) toggleFeedPanel(false);
|
||||
});
|
||||
|
||||
//Feed Panel
|
||||
initPosts();
|
||||
|
||||
//Settings Panel
|
||||
initSettings();
|
||||
|
||||
//project Bootstrap
|
||||
initProject(asHash.items[0]);
|
||||
}
|
||||
@@ -163,34 +206,36 @@ function initProject(sProjectCodeName){
|
||||
mimeType: 'application/json'
|
||||
})
|
||||
).done(function(aoMessages, aoTracks) {
|
||||
initSpotMessages(aoMessages[0]['data'] || [], aoTracks[0]);
|
||||
initSpotMessages(aoMessages[0]['data'] || [], aoTracks[0], aoMessages[0]['desc']=='No Data');
|
||||
});
|
||||
|
||||
//Posts
|
||||
initPosts();
|
||||
//Show/Hide Poster Panel
|
||||
var bHistoMode = (self.vars(['project', 'mode']) == self.consts.modes.histo);
|
||||
$('#poster').toggle(!bHistoMode);
|
||||
|
||||
//Feed auto-update
|
||||
self.tmp('simple-bar').getScrollElement().scrollTop = 0;
|
||||
if(!bHistoMode) onAutoUpdate(true);
|
||||
else updateFeed(true);
|
||||
}
|
||||
|
||||
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: oSpot.lang('post_new_message')
|
||||
};
|
||||
getPost(asPoster).appendTo($Poster.show());
|
||||
getPost(asPoster).appendTo($('#poster'));
|
||||
|
||||
autosize($('#post'));
|
||||
|
||||
$('#submit').click(function(){
|
||||
if($Poster.checkForm())
|
||||
if($('#poster').checkForm())
|
||||
{
|
||||
self.get(
|
||||
'add_post',
|
||||
function()
|
||||
{
|
||||
$('#name').val('');
|
||||
$('#post').val('');
|
||||
updateFeed(true);
|
||||
},
|
||||
@@ -204,10 +249,71 @@ function initPosts() {
|
||||
});
|
||||
}
|
||||
|
||||
//Feed auto-update
|
||||
self.tmp('simple-bar').getScrollElement().scrollTop = 0;
|
||||
if(self.vars(['project', 'mode']) != self.consts.modes.histo) onAutoUpdate(true);
|
||||
else updateFeed(true);
|
||||
function initSettings(){
|
||||
//Scrollbar
|
||||
new SimpleBar($('#settings-sections')[0]);
|
||||
|
||||
//Feedback display function
|
||||
var settingsFeedback = function(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').click(function(){
|
||||
var sAction = $(this).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 is not an email');
|
||||
else {
|
||||
oSpot.get(
|
||||
sAction,
|
||||
function(asData) {
|
||||
settingsFeedback('success', oSpot.lang('nl_'+sAction+'d'));
|
||||
oSpot.vars('user', asData);
|
||||
setUserInterface();
|
||||
},
|
||||
{'email': sEmail},
|
||||
function(sDesc) {settingsFeedback('error', sDesc);},
|
||||
function(sState) {
|
||||
var bLoading = (sState=='start');
|
||||
$('#nl_btn')
|
||||
.prop('disabled', bLoading)
|
||||
.toggleClass('loading', bLoading);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
//Twink interface with user data
|
||||
setUserInterface();
|
||||
}
|
||||
|
||||
function setUserInterface() {
|
||||
var asUserInfo = oSpot.vars('user');
|
||||
if(asUserInfo.id_user) { //Subscribed User
|
||||
$('#email').val(asUserInfo.email).prop('disabled', true);
|
||||
$('#nl_btn').attr({name: 'unsubscribe', title: oSpot.lang('nl_unsubscribe'), 'class':'unsubscribe'});
|
||||
$('#nl_desc').text(oSpot.lang('nl_subscribed_desc'));
|
||||
|
||||
//Populate nickname
|
||||
if(asUserInfo.name) $('#name').val(asUserInfo.name);
|
||||
}
|
||||
else { //Unsubscribed User
|
||||
//Switch to subcribe mode
|
||||
$('#email').val('').prop('disabled', false);
|
||||
$('#nl_btn').attr({name: 'subscribe', title: oSpot.lang('nl_subscribe'), 'class':'subscribe'});
|
||||
$('#nl_desc').text(oSpot.lang('nl_unsubscribed_desc'));
|
||||
|
||||
//Reset nickname
|
||||
$('#name').val('');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onAutoUpdate(bFirstExec) {
|
||||
@@ -218,11 +324,17 @@ function onAutoUpdate(bFirstExec) {
|
||||
}
|
||||
|
||||
function getElevWidth() {
|
||||
// Page widthFeed Panel Legend width (bottom left) Elevation right margin (from page side)
|
||||
return $('#projects').width() - oSpot.tmp('feed_width') - $('.leaflet-bottom.leaflet-left > .leaflet-control-layers').outerWidth(true) - parseInt($('.leaflet-bottom.leaflet-right > .leaflet-control-scale').css('margin-right').slice(0, -2));
|
||||
var
|
||||
iPageWidth = $('#projects').width(),
|
||||
iFeedPanelWidth = oSpot.tmp('feed_width'),
|
||||
iSettingsPanelWidth = isSettingsPanelOpen()?$('#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;
|
||||
}
|
||||
|
||||
function initSpotMessages(aoMessages, aoTracks) {
|
||||
function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
|
||||
|
||||
//Tile layers
|
||||
aoLayers = {};
|
||||
@@ -241,6 +353,13 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
});
|
||||
self.tmp('map', oMap);
|
||||
|
||||
//Controls: Settings Panel
|
||||
var oSettingsPanel = L.control({position: 'topleft'});
|
||||
var $SettingsButton = $('#settings-button').clone();
|
||||
$SettingsButton.click(toggleSettingsPanel);
|
||||
oSettingsPanel.onAdd = function(oMap) {return $SettingsButton[0];};
|
||||
oSettingsPanel.addTo(oMap);
|
||||
|
||||
//Controls: Feed Panel
|
||||
var oFeedPanel = L.control({position: 'topright'});
|
||||
var $PostButton = $('#post-button').clone();
|
||||
@@ -254,27 +373,23 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
oLegend.addTo(oMap);
|
||||
|
||||
//Controls: 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(){
|
||||
toggleSettingsPanel(false);
|
||||
self.setHash(self.vars('page'), [$(this).val()]);
|
||||
});
|
||||
|
||||
var $Label = $('<label>').append($('<div>')
|
||||
.append($Radio)
|
||||
.append($('<span>').text(' '+asProject.name))
|
||||
.append($('<a>', {'class':'fa fa-download push-left', href:asProject.gpxfilepath}).click(function(e){e.stopPropagation();}))
|
||||
.append($('<a>', {'class':'fa fa-download push-left', href:asProject.gpxfilepath, title:oSpot.lang('track_download')}).click(function(e){e.stopPropagation();}))
|
||||
);
|
||||
$Labels.append($Label);
|
||||
});
|
||||
|
||||
return $('<div>', {'class':'leaflet-control-layers leaflet-control leaflet-control-layers-expanded'}).append($('<section>').append($Labels))[0];
|
||||
};
|
||||
oProjects.addTo(oMap);
|
||||
$('#settings-projects').empty().append($Labels);
|
||||
|
||||
//Controls: Scale
|
||||
oScale = L.control.scale({imperial: false, 'position':'bottomright'}).addTo(oMap);
|
||||
@@ -295,8 +410,9 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
}).addTo(oMap);
|
||||
self.tmp('elev', oElev);
|
||||
|
||||
//Controls: Tiles (layers)
|
||||
//Controls: Tiles (layers): Add & Move to Settings Panel
|
||||
L.control.layers(aoLayers, null, {position: 'topleft'}).addTo(oMap);
|
||||
$('#layers').empty().append($('.leaflet-control-layers-list .leaflet-control-layers-base'));
|
||||
|
||||
//Tracks, colors & popup
|
||||
var oActualTracks = L.geoJson(aoTracks, {
|
||||
@@ -305,7 +421,7 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
}
|
||||
}).addTo(oMap);
|
||||
|
||||
var oTracks = L.geoJson(aoTracks, {
|
||||
self.tmp('track', L.geoJson(aoTracks, {
|
||||
style: {weight: 20, opacity: 0},
|
||||
onEachFeature: function(feature, oLayer) {
|
||||
var asProperties = feature.properties;
|
||||
@@ -392,19 +508,18 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
(oElev.addData.bind(oElev))(feature, oLayer);
|
||||
}
|
||||
}
|
||||
}).addTo(oMap);
|
||||
}).addTo(oMap));
|
||||
|
||||
//Centering map
|
||||
var bWithFeedPanel = (!bNoFeed && !isMobile());
|
||||
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'));
|
||||
oMap.panBy([(bWithFeedPanel?1:0)*$('#feed').outerWidth(true)/2, 0]);
|
||||
}
|
||||
else oMap.fitBounds(oTracks.getBounds(), {paddingTopLeft: L.point(5, self.tmp('marker_size').height + 5), paddingBottomRight: L.point(self.tmp('feed_width') + 5, 5)});
|
||||
else oMap.fitBounds(self.tmp('track').getBounds(), {paddingTopLeft: L.point(5, self.tmp('marker_size').height + 5), paddingBottomRight: L.point(5 + parseInt(bWithFeedPanel?$('#feed').outerWidth(true):0), 5)});
|
||||
|
||||
//Spot Messages
|
||||
$.each(aoMessages, function(iKey, oMsg){
|
||||
@@ -426,7 +541,7 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
.append(oMsg.formatted_time+(self.vars(['project', 'mode'])==self.consts.modes.blog?' ('+oMsg.relative_time+')':'')+self.tmp('site_tz_notice')))
|
||||
.append($('<p>', {'class':'coordinates'})
|
||||
.addIcon('fa-coords fa-fw fa-lg', false)
|
||||
.append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
|
||||
.append(getGoogleMapsLink(oMsg)));
|
||||
|
||||
//Tooltip medias
|
||||
if(oMsg.medias) {
|
||||
@@ -440,14 +555,14 @@ function initSpotMessages(aoMessages, aoTracks) {
|
||||
}
|
||||
|
||||
oMarker.bindPopup($Tooltip[0], {
|
||||
maxWidth: 1000,
|
||||
maxWidth: $('#projects').width(),
|
||||
autoPan: false,
|
||||
closeOnClick: true,
|
||||
offset: new L.Point(0, -30)
|
||||
});
|
||||
|
||||
//Open tooltip on latest message in mobile mode
|
||||
if(iKey === (aoMessages.length - 1) && self.vars(['project', 'mode']) == self.consts.modes.blog && (!oMsg.medias || oMsg.medias.length < 3) && self.tmp('mobile')) oMarker.openPopup();
|
||||
if(iKey === (aoMessages.length - 1) && self.vars(['project', 'mode']) == self.consts.modes.blog && (!oMsg.medias || oMsg.medias.length < 3) && isMobile()) oMarker.openPopup();
|
||||
|
||||
oSpot.tmp(['markers', oMsg.id_message], oMarker);
|
||||
});
|
||||
@@ -498,7 +613,7 @@ function updateFeed(bFirstChunk, bDiscrete) {
|
||||
|
||||
self.tmp('updatable', true);
|
||||
|
||||
if(bFirstChunk && !self.tmp('mobile') && !$.isEmptyObject(asData)) toggleFeedPanel(true, false);
|
||||
if(bFirstChunk && !isMobile() && !$.isEmptyObject(asData)) toggleFeedPanel(true, 'none');
|
||||
}, {
|
||||
'project_id': self.vars(['project', 'id']),
|
||||
'chunk': self.tmp('news_chunk')
|
||||
@@ -517,7 +632,7 @@ function setFeedUpdateTimer(iSeconds, fCallback) {
|
||||
function getPost(asPost) {
|
||||
asPost.headerless = asPost.headerless || false;
|
||||
var $Post = $('<div>', {'class':'post '+asPost.type+(asPost.headerless?' headerless':'')});
|
||||
var sRelTime = (asPost.relative_time!='')?((self.vars(['project', 'mode'])==self.consts.modes.histo)?asPost.formatted_time.substr(0, 10):asPost.relative_time):'';
|
||||
var sRelTime = (asPost.relative_time!='')?((self.vars('project') && self.vars(['project', 'mode'])==self.consts.modes.histo)?asPost.formatted_time.substr(0, 10):asPost.relative_time):'';
|
||||
var sAbsTime = asPost.formatted_time;
|
||||
var sType = asPost.subtype || asPost.type;
|
||||
var $Body = {};
|
||||
@@ -526,7 +641,7 @@ function getPost(asPost) {
|
||||
case 'message':
|
||||
$Body = $('<div>')
|
||||
.data('id', asPost.id_message)
|
||||
.append($('<p>').addIcon('fa-coords', true).append(asPost.lat_dms+' '+asPost.lon_dms))
|
||||
.append($('<p>').addIcon('fa-coords', true).append(getGoogleMapsLink(asPost)))
|
||||
.append($('<p>').addIcon('fa-time', true).append(sAbsTime+self.tmp('site_tz_notice')))
|
||||
.append($('<a>', {'class':'drill'})
|
||||
.append($('<img>', {'class':'staticmap', title: oSpot.lang('click_zoom'), src: getWmtsApiUrl('static', asPost.latitude, asPost.longitude, 13)}))
|
||||
@@ -536,9 +651,17 @@ function getPost(asPost) {
|
||||
)
|
||||
.click(function(){
|
||||
var oMarker = oSpot.tmp(['markers', $(this).parent().data('id')]);
|
||||
self.tmp('map').setOffsetView(self.tmp('map_offset'), oMarker.getLatLng(), 13);
|
||||
if(isMobile()) {
|
||||
toggleFeedPanel(false, 'panToInstant');
|
||||
self.tmp('map').setView(oMarker.getLatLng(), 15);
|
||||
}
|
||||
else {
|
||||
var iOffset = $('#feed').outerWidth(true)/2 - ($('#projects').hasClass('with-settings')?1:-1)*$('#settings').outerWidth(true)/2;
|
||||
var iRatio = -1 * iOffset / $('body').outerWidth(true);
|
||||
self.tmp('map').setOffsetView(iRatio, oMarker.getLatLng(), 15);
|
||||
}
|
||||
|
||||
if(!oMarker.isPopupOpen()) oMarker.openPopup();
|
||||
if(self.tmp('mobile')) toggleFeedPanel(false);
|
||||
})
|
||||
)
|
||||
.hover(
|
||||
@@ -615,4 +738,13 @@ function getMediaLink(asData, sType) {
|
||||
|
||||
return $Link;
|
||||
}
|
||||
|
||||
function getGoogleMapsLink(asInfo) {
|
||||
return $('<a>', {
|
||||
href:'https://www.google.com/maps/place/'+asInfo.lat_dms+'+'+asInfo.lon_dms+'/@'+asInfo.latitude+','+asInfo.longitude+',10z',
|
||||
title: oSpot.lang('see_on_google'),
|
||||
target: '_blank',
|
||||
rel: 'noreferrer'
|
||||
}).text(asInfo.lat_dms+' '+asInfo.lon_dms);
|
||||
}
|
||||
</script>
|
||||
@@ -13,3 +13,4 @@
|
||||
* Reset zoom on image closing (lightbox)
|
||||
* Fix video fullscreen button on ios
|
||||
* Fix lightbox portrait mode: push text under
|
||||
* Subscribe to message feed
|
||||
@@ -65,12 +65,12 @@ function Spot(asGlobals)
|
||||
|
||||
/* Interface with server */
|
||||
|
||||
this.get = function(sAction, fOnSuccess, oVars, fOnError, bProcessIcon)
|
||||
this.get = function(sAction, fOnSuccess, oVars, fOnError, fonProgress)
|
||||
{
|
||||
if(!oVars) oVars = {};
|
||||
fOnError = fOnError || function(sError) {console.log(sError);};
|
||||
bProcessIcon = bProcessIcon || false;
|
||||
//if(bProcessIcon) self.addBufferIcon();
|
||||
fonProgress = fonProgress || function(sState){};
|
||||
fonProgress('start');
|
||||
|
||||
oVars['a'] = sAction;
|
||||
oVars['t'] = self.consts.timezone;
|
||||
@@ -82,18 +82,15 @@ function Spot(asGlobals)
|
||||
})
|
||||
.done(function(oData)
|
||||
{
|
||||
fonProgress('done');
|
||||
if(oData.desc.substr(0, self.consts.lang_prefix.length)==self.consts.lang_prefix) oData.desc = self.lang(oData.desc.substr(5));
|
||||
|
||||
if(oData.result==self.consts.error) fOnError(oData.desc);
|
||||
else
|
||||
{
|
||||
//if(bProcessIcon) self.resetIcon();
|
||||
fOnSuccess(oData.data);
|
||||
}
|
||||
else fOnSuccess(oData.data);
|
||||
})
|
||||
.fail(function(jqXHR, textStatus, errorThrown)
|
||||
{
|
||||
//if(bProcessIcon) self.resetIcon();
|
||||
fonProgress('fail');
|
||||
fOnError(textStatus+' '+errorThrown);
|
||||
});
|
||||
};
|
||||
@@ -171,7 +168,7 @@ function Spot(asGlobals)
|
||||
self.onSamePageMove = function(asHash){return false};
|
||||
self.onQuitPage = function(){return true};
|
||||
self.onResize = function(){};
|
||||
self.onFeedback = function(sType, sMsg){feedback(sType, sMsg);};
|
||||
self.onFeedback = function(sType, sMsg){};
|
||||
self.onKeydown = function(oEvent){};
|
||||
};
|
||||
|
||||
|
||||
@@ -57,3 +57,26 @@ button {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input, textarea, button {
|
||||
border: none;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Feedback */
|
||||
|
||||
.feedback {
|
||||
p {
|
||||
margin: 0 0 1em 0;
|
||||
&.error {
|
||||
color: red;
|
||||
}
|
||||
&.warning {
|
||||
color: orange;
|
||||
}
|
||||
&.success {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,10 @@ $fa-css-prefix: fa;
|
||||
@extend .fas;
|
||||
|
||||
&.push {
|
||||
margin-right: 0.5em;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
&.push-left {
|
||||
margin-left: 0.5em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,15 @@ $fa-css-prefix: fa;
|
||||
.#{$fa-css-prefix}-elev-gain:before { content: fa-content($fa-var-arrow-circle-up); }
|
||||
.#{$fa-css-prefix}-download:before { content: fa-content($fa-var-file-download); }
|
||||
|
||||
/* Settings */
|
||||
.#{$fa-css-prefix}-menu:before { content: fa-content($fa-var-bars); }
|
||||
.#{$fa-css-prefix}-newsletter:before { content: fa-content($fa-var-wifi); }
|
||||
.#{$fa-css-prefix}-project:before { content: fa-content($fa-var-hiking); }
|
||||
.#{$fa-css-prefix}-error:before { content: fa-content($fa-var-exclamation-square); }
|
||||
.#{$fa-css-prefix}-warning:before { content: fa-content($fa-var-exclamation-triangle); }
|
||||
.#{$fa-css-prefix}-success:before { content: fa-content($fa-var-check-circle); }
|
||||
.#{$fa-css-prefix}-unsubscribe:before { content: fa-content($fa-var-times); }
|
||||
|
||||
/* Feed */
|
||||
.#{$fa-css-prefix}-post:before { content: fa-content($fa-var-comment); }
|
||||
.#{$fa-css-prefix}-media:before { content: fa-content($fa-var-photo-video); }
|
||||
|
||||
@@ -26,14 +26,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#feedback {
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,34 @@
|
||||
//Feed width
|
||||
$block-spacing: 1rem;
|
||||
$feed-width: 30%;
|
||||
$feed-width-max: "400px + 3 * #{$block-spacing}";
|
||||
$panel-width: 30%;
|
||||
$panel-width-max: "400px + 3 * #{$block-spacing}";
|
||||
|
||||
//Feed colors
|
||||
$post-input-bg: #d9deff;
|
||||
$post-color: #323268;
|
||||
$post-bg: #B4BDFF;
|
||||
$post-input-bg: #ffffff; //#d9deff;
|
||||
$post-color: #333; //#323268;
|
||||
$post-bg: rgba(255,255,255,.8); //#B4BDFF;
|
||||
$message-color: #326526;
|
||||
$message-bg: #6DFF58;
|
||||
$media-color: #635C28;
|
||||
$media-bg: #F3EC9F;
|
||||
$media-color: #333; //#635C28;
|
||||
$media-bg: rgba(255,255,255,.8); //#F3EC9F;
|
||||
|
||||
//Legend colors
|
||||
$track-main-color: #00ff78;
|
||||
$track-off-track-color: #0000ff;
|
||||
$track-hitchhiking-color: #FF7814;
|
||||
$legend-color: #222;
|
||||
$legend-color: $post-color;
|
||||
|
||||
#projects {
|
||||
|
||||
&.with-feed {
|
||||
#submap {
|
||||
width: calc(100% - #{$feed-width});
|
||||
min-width: calc(100% - #{$feed-width-max});
|
||||
width: calc(100% - #{$panel-width});
|
||||
min-width: calc(100% - #{$panel-width-max});
|
||||
}
|
||||
|
||||
.leaflet-right {
|
||||
width: calc(#{$feed-width});
|
||||
max-width: calc(#{$feed-width-max});
|
||||
width: calc(#{$panel-width});
|
||||
max-width: calc(#{$panel-width-max});
|
||||
}
|
||||
|
||||
#feed {
|
||||
@@ -47,6 +47,39 @@ $legend-color: #222;
|
||||
right: -100%;
|
||||
}
|
||||
}
|
||||
&.with-settings {
|
||||
#submap {
|
||||
width: calc(100% - #{$panel-width});
|
||||
min-width: calc(100% - #{$panel-width-max});
|
||||
}
|
||||
|
||||
.leaflet-left {
|
||||
width: calc(#{$panel-width});
|
||||
max-width: calc(#{$panel-width-max});
|
||||
}
|
||||
|
||||
#settings {
|
||||
z-index: 999;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#settings-button {
|
||||
.fa {
|
||||
@extend .fa-prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:not(.with-settings) {
|
||||
#settings #settings-sections {
|
||||
left: -100%;
|
||||
}
|
||||
}
|
||||
&.with-feed.with-settings {
|
||||
#submap {
|
||||
width: calc(100% - #{$panel-width} * 2);
|
||||
min-width: calc(100% - #{$panel-width-max} * 2);
|
||||
}
|
||||
}
|
||||
|
||||
#submap {
|
||||
position: absolute;
|
||||
@@ -103,19 +136,6 @@ $legend-color: #222;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&.leaflet-control-layers-expanded {
|
||||
color: $legend-color;
|
||||
width: calc(100% - 2em - 16px);
|
||||
|
||||
a.fa-download {
|
||||
color: $legend-color;
|
||||
|
||||
&:hover {
|
||||
color: #0078A8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.leaflet-control-scale {
|
||||
padding: 0.5em;
|
||||
|
||||
@@ -125,30 +145,27 @@ $legend-color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull right controls by $feed-width % */
|
||||
.leaflet-right {
|
||||
/* Pull right/left controls by $panel-width */
|
||||
.leaflet-right, .leaflet-left {
|
||||
transition: all 0.5s;
|
||||
width: 0;
|
||||
max-width: 0;
|
||||
|
||||
.leaflet-control {
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
left: -100%;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
right: -100%;
|
||||
}
|
||||
|
||||
/* Replace Layers image with font awesome icon */
|
||||
.leaflet-control-layers-toggle {
|
||||
@extend .control-icon;
|
||||
@extend .fa-layers;
|
||||
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
/* Hide default layer control */
|
||||
.leaflet-top.leaflet-left .leaflet-control-layers {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#legend {
|
||||
.track {
|
||||
white-space: nowrap;
|
||||
.line {
|
||||
width: 2em;
|
||||
height: 4px;
|
||||
@@ -175,7 +192,7 @@ $legend-color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
#post-button {
|
||||
#post-button, #settings-button {
|
||||
cursor: pointer;
|
||||
text-shadow: 0px 1px 1px rgba(0,0,0,0.8);
|
||||
width: 44px;
|
||||
@@ -188,8 +205,13 @@ $legend-color: #222;
|
||||
.fa {
|
||||
color: #CCC;
|
||||
@extend .control-icon;
|
||||
}
|
||||
}
|
||||
#post-button .fa {
|
||||
@extend .fa-post;
|
||||
}
|
||||
#settings-button .fa {
|
||||
@extend .fa-menu;
|
||||
}
|
||||
|
||||
/* Drill & Map icons */
|
||||
@@ -235,25 +257,21 @@ $legend-color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
/* Feed Panel */
|
||||
/* Feed/Settings Panel */
|
||||
|
||||
#feed {
|
||||
#feed, #settings {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: calc(#{$feed-width});
|
||||
max-width: calc(#{$feed-width-max});
|
||||
width: calc(#{$panel-width});
|
||||
max-width: calc(#{$panel-width-max});
|
||||
z-index: -1;
|
||||
transition-property: z-index;
|
||||
transition-duration: 0.1s;
|
||||
transition-delay: 0.5s;
|
||||
overflow: hidden;
|
||||
|
||||
input, textarea, button {
|
||||
border: none;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 3px;
|
||||
input, textarea {
|
||||
background-color: $post-input-bg;
|
||||
color: $post-color;
|
||||
}
|
||||
@@ -267,6 +285,9 @@ $legend-color: #222;
|
||||
color: $post-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
#feed {
|
||||
right: 0;
|
||||
|
||||
#posts {
|
||||
position: absolute;
|
||||
@@ -391,6 +412,10 @@ $legend-color: #222;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $message-color;
|
||||
}
|
||||
|
||||
a.drill {
|
||||
.drill-icon {
|
||||
transform: translate(-16px, -32px);
|
||||
@@ -432,13 +457,94 @@ $legend-color: #222;
|
||||
}
|
||||
}
|
||||
}
|
||||
#settings {
|
||||
left: 0;
|
||||
|
||||
#settings-sections {
|
||||
width: calc(100% - 3rem);
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
box-shadow: 2px 2px 3px 0px rgba(0,0,0,.5);
|
||||
position: absolute;
|
||||
transition: all 0.5s;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
color: $post-color;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
|
||||
.settings-section {
|
||||
h1 {
|
||||
margin: 1.5rem 0 1rem;
|
||||
}
|
||||
&:first-child h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: .3em;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.newsletter {
|
||||
input#email {
|
||||
width: calc(100% - 6em);
|
||||
|
||||
&:disabled {
|
||||
color: #999;
|
||||
background: rgba(255,255,255,0.2);
|
||||
}
|
||||
}
|
||||
button#nl_btn {
|
||||
margin-left: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
&.subscribe .fa {
|
||||
@extend .fa-send;
|
||||
}
|
||||
&.unsubscribe .fa {
|
||||
@extend .fa-unsubscribe;
|
||||
}
|
||||
&.loading {
|
||||
background-color: $message-color;
|
||||
color: white;
|
||||
span {
|
||||
@extend .flicker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#settings-projects {
|
||||
a.fa-download {
|
||||
color: $legend-color;
|
||||
|
||||
&:hover {
|
||||
color: #0078A8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Info Window */
|
||||
|
||||
.leaflet-popup-content {
|
||||
margin: 0;
|
||||
|
||||
.info-window {
|
||||
margin: 1rem;
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
margin: 1em 0;
|
||||
margin: 1em 0 1.2em;
|
||||
|
||||
i {
|
||||
margin-right: 0.3125em;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -448,15 +554,24 @@ $legend-color: #222;
|
||||
i {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $post-color;
|
||||
}
|
||||
}
|
||||
|
||||
.medias {
|
||||
margin-top: 0.5em;
|
||||
margin-top: -0.5rem;
|
||||
line-height: 0;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
|
||||
margin-right: 1rem;
|
||||
margin-top: 1rem;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.drill {
|
||||
font-size: 2em;
|
||||
@@ -486,6 +601,7 @@ $legend-color: #222;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elems {
|
||||
display: none;
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
#projects {
|
||||
|
||||
&.with-feed {
|
||||
&.with-feed, &.with-settings {
|
||||
#submap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.leaflet-right {
|
||||
.leaflet-right, .leaflet-left {
|
||||
width: calc(100% - 44px - 2 * #{$block-spacing});
|
||||
}
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
}
|
||||
|
||||
.leaflet-control-container {
|
||||
.leaflet-top.leaflet-left,
|
||||
.leaflet-bottom.leaflet-left,
|
||||
.leaflet-bottom.leaflet-right .leaflet-control.elevation {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#feed {
|
||||
#feed, #settings {
|
||||
width: calc(100% - 44px - 2 * #{$block-spacing});
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user