Adding Last Update

This commit is contained in:
2020-08-14 14:58:29 +02:00
parent 84eff9f807
commit 354f824588
12 changed files with 505 additions and 329 deletions

17
images/spot-logo-only.svg Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg width="406" height="469" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 406 469" style="enable-background:new 0 0 406 469;">
<style type="text/css">.st0{fill:#F18A00;}</style>
<g id="Layer_3"/>
<g id="Layer_8"/>
<g id="Layer_9"/>
<g id="Layer_7"/>
<g id="Layer_6"/>
<g id="Layer_4"/>
<g>
<g>
<path class="st0" d="m85.80618,195.80013c-1,-0.8 -1.3,-2.3 -0.6,-3.4c11.1,-18.2 56.5,-85.8 117.3,-85.8c49.6,0 90.4,33.4 110.3,53.3c1.2,1.2 2.9,1.9 4.6,1.9c1.7,0 3.4,-0.7 4.6,-1.9l16.4,-16.3c1,-1 1.1,-2.5 0.2,-3.5c-5.1,-6.1 -15.3,-17.2 -29.8,-28.2c-31.7,-24.1 -67.5,-36.3 -106.3,-36.3c-79.4,0 -129.8,75.4 -142.3,96.5c-0.8,1.4 -2.6,1.7 -3.8,0.7l-55.4,-43.6c-1,-0.8 -1.3,-2.2 -0.7,-3.3c5.9,-10.8 23,-39.4 48.5,-63.7c42.8,-40.7 95.1,-62.2 151.4,-62.2c56,0 109.2,18.9 153.9,54.8c27.3,21.9 45.5,43.2 51.3,50.4c0.8,1 0.7,2.5 -0.2,3.5l-12.3,12.2c-1.1,1.1 -2.9,1 -3.8,-0.2c-7.3,-8.9 -26.2,-30.5 -49.7,-49.2c-41.1,-32.7 -88,-49.2 -139.2,-49.2c-50.9,0 -96.5,18.6 -135.4,55.4c-20.9,19.7 -30.4,34.1 -35.6,42.3c-0.7,1.1 -0.5,2.6 0.6,3.5l16.7,13.2c1.2,0.9 2.6,1.4 4.1,1.4c2.2,0 4.2,-1.1 5.4,-2.9c7.4,-10.7 15.9,-20.6 26.8,-31.1c34.3,-33.1 75.7,-50.6 119.9,-50.6c92,0 151.2,70.7 165.3,89.4c0.8,1.1 0.7,2.5 -0.3,3.4l-49.8,49.3c-1.1,1.1 -2.8,1 -3.8,-0.1c-15.9,-18.1 -63,-66.5 -111.4,-66.5c-23.6,0 -46.6,11.3 -68.4,33.5c-7.2,7.3 -13.9,15.6 -19.9,24.4c-0.8,1.1 -0.5,2.7 0.6,3.6l93.1,73.2c1.1,0.9 1.3,2.5 0.4,3.7l-10.5,13.3c-0.9,1.1 -2.5,1.3 -3.7,0.4l-108.5,-85.3z" />
<path class="st0" d="m205.90618,468.90013c-56,0 -109.2,-18.9 -153.9,-54.8c-27.3,-21.9 -45.5,-43.2 -51.3,-50.4c-0.8,-1 -0.7,-2.5 0.2,-3.5l12.3,-12.2c1.1,-1.1 2.9,-1 3.8,0.2c7.3,8.9 26.2,30.5 49.7,49.2c41.1,32.7 88,49.2 139.2,49.2c50.9,0 96.5,-18.6 135.4,-55.4c20.9,-19.7 30.4,-34.1 35.6,-42.3c0.7,-1.1 0.5,-2.6 -0.6,-3.5l-16.7,-13.2c-1.2,-0.9 -2.6,-1.4 -4.1,-1.4c-2.2,0 -4.2,1.1 -5.4,2.9c-7.4,10.7 -15.9,20.6 -26.8,31.1c-34.3,33.1 -75.7,50.6 -119.8,50.6c-92,0 -151.2,-70.7 -165.3,-89.4c-0.8,-1.1 -0.7,-2.5 0.3,-3.4l49.8,-49.3c1.1,-1.1 2.8,-1 3.8,0.1c15.9,18.1 63,66.5 111.4,66.5c23.6,0 46.6,-11.3 68.4,-33.5c7.2,-7.3 13.9,-15.6 19.9,-24.4c0.8,-1.1 0.5,-2.7 -0.6,-3.6l-93.1,-73.2c-1.1,-0.9 -1.3,-2.5 -0.4,-3.7l10.5,-13.3c0.9,-1.1 2.5,-1.3 3.7,-0.4l108.3,85.2c1,0.8 1.3,2.3 0.6,3.4c-11.1,18.2 -56.5,85.8 -117.3,85.8c-49.6,0 -90.4,-33.4 -110.3,-53.3c-1.2,-1.2 -2.9,-1.9 -4.6,-1.9s-3.4,0.7 -4.6,1.9l-16.4,16.3c-1,1 -1.1,2.5 -0.2,3.5c5.1,6.1 15.3,17.2 29.8,28.2c31.7,24.1 67.5,36.3 106.3,36.3c79.4,0 129.8,-75.4 142.3,-96.5c0.8,-1.4 2.6,-1.7 3.8,-0.7l55.4,43.6c1,0.8 1.3,2.2 0.7,3.3c-5.9,10.8 -23,39.4 -48.5,63.7c-42.6,40.8 -95,62.3 -151.3,62.3z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

48
images/spot-logo.svg Normal file
View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1466.5 481.4" style="enable-background:new 0 0 1466.5 481.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F18A00;}
.st1{fill:#FFFFFF;}
</style>
<g id="Layer_3">
</g>
<g id="Layer_8">
</g>
<g id="Layer_9">
</g>
<g id="Layer_7">
</g>
<g id="Layer_6">
</g>
<g id="Layer_4">
</g>
<g>
<g>
<path class="st0" d="M99.7,202.1c-1-0.8-1.3-2.3-0.6-3.4c11.1-18.2,56.5-85.8,117.3-85.8c49.6,0,90.4,33.4,110.3,53.3 c1.2,1.2,2.9,1.9,4.6,1.9c1.7,0,3.4-0.7,4.6-1.9l16.4-16.3c1-1,1.1-2.5,0.2-3.5c-5.1-6.1-15.3-17.2-29.8-28.2 C291,94.1,255.2,81.9,216.4,81.9c-79.4,0-129.8,75.4-142.3,96.5c-0.8,1.4-2.6,1.7-3.8,0.7l-55.4-43.6c-1-0.8-1.3-2.2-0.7-3.3 c5.9-10.8,23-39.4,48.5-63.7c42.8-40.7,95.1-62.2,151.4-62.2c56,0,109.2,18.9,153.9,54.8c27.3,21.9,45.5,43.2,51.3,50.4 c0.8,1,0.7,2.5-0.2,3.5l-12.3,12.2c-1.1,1.1-2.9,1-3.8-0.2c-7.3-8.9-26.2-30.5-49.7-49.2c-41.1-32.7-88-49.2-139.2-49.2 c-50.9,0-96.5,18.6-135.4,55.4c-20.9,19.7-30.4,34.1-35.6,42.3c-0.7,1.1-0.5,2.6,0.6,3.5L60.4,143c1.2,0.9,2.6,1.4,4.1,1.4 c2.2,0,4.2-1.1,5.4-2.9c7.4-10.7,15.9-20.6,26.8-31.1c34.3-33.1,75.7-50.6,119.9-50.6c92,0,151.2,70.7,165.3,89.4 c0.8,1.1,0.7,2.5-0.3,3.4l-49.8,49.3c-1.1,1.1-2.8,1-3.8-0.1c-15.9-18.1-63-66.5-111.4-66.5c-23.6,0-46.6,11.3-68.4,33.5 c-7.2,7.3-13.9,15.6-19.9,24.4c-0.8,1.1-0.5,2.7,0.6,3.6L222,270c1.1,0.9,1.3,2.5,0.4,3.7L211.9,287c-0.9,1.1-2.5,1.3-3.7,0.4 L99.7,202.1z"/>
<path class="st0" d="M219.8,475.2c-56,0-109.2-18.9-153.9-54.8c-27.3-21.9-45.5-43.2-51.3-50.4c-0.8-1-0.7-2.5,0.2-3.5l12.3-12.2 c1.1-1.1,2.9-1,3.8,0.2c7.3,8.9,26.2,30.5,49.7,49.2c41.1,32.7,88,49.2,139.2,49.2c50.9,0,96.5-18.6,135.4-55.4 c20.9-19.7,30.4-34.1,35.6-42.3c0.7-1.1,0.5-2.6-0.6-3.5l-16.7-13.2c-1.2-0.9-2.6-1.4-4.1-1.4c-2.2,0-4.2,1.1-5.4,2.9 c-7.4,10.7-15.9,20.6-26.8,31.1c-34.3,33.1-75.7,50.6-119.8,50.6c-92,0-151.2-70.7-165.3-89.4c-0.8-1.1-0.7-2.5,0.3-3.4l49.8-49.3 c1.1-1.1,2.8-1,3.8,0.1c15.9,18.1,63,66.5,111.4,66.5c23.6,0,46.6-11.3,68.4-33.5c7.2-7.3,13.9-15.6,19.9-24.4 c0.8-1.1,0.5-2.7-0.6-3.6L212,211.5c-1.1-0.9-1.3-2.5-0.4-3.7l10.5-13.3c0.9-1.1,2.5-1.3,3.7-0.4l108.3,85.2 c1,0.8,1.3,2.3,0.6,3.4c-11.1,18.2-56.5,85.8-117.3,85.8c-49.6,0-90.4-33.4-110.3-53.3c-1.2-1.2-2.9-1.9-4.6-1.9s-3.4,0.7-4.6,1.9 l-16.4,16.3c-1,1-1.1,2.5-0.2,3.5c5.1,6.1,15.3,17.2,29.8,28.2c31.7,24.1,67.5,36.3,106.3,36.3c79.4,0,129.8-75.4,142.3-96.5 c0.8-1.4,2.6-1.7,3.8-0.7l55.4,43.6c1,0.8,1.3,2.2,0.7,3.3c-5.9,10.8-23,39.4-48.5,63.7C328.5,453.7,276.1,475.2,219.8,475.2z"/>
</g>
<g>
<g>
<g>
<path class="st1" d="M543.5,95.1c-1.3-4.8-3.7-8.6-8.4-8.6c-3.6,0-5.9,2.1-5.9,5.4c0,13.9,31.3,12,31.3,38.3 c0,14.2-10,22.3-25.4,22.3c-15,0-22.4-6.7-25-21.3L527,128c0.8,6.1,3.3,9.9,8.9,9.9c4.1,0,7.1-2.1,7.1-6.4 c0-14.6-31.4-12.6-31.4-39c0-12.3,8.9-20.6,23.2-20.6c14.9,0,21.5,7.6,24.4,19.9L543.5,95.1z"/>
<path class="st1" d="M620,151.5l-3-17h-14.9l-2.7,17h-18.6l16.9-78.6h23.9l17.3,78.6H620z M604.7,119h9.7l-4.8-28.2h-0.3 L604.7,119z"/>
<path class="st1" d="M674.5,151.5l-18.1-78.6h19.8l8.8,52h0.2l9.6-52h19.6l-18.5,78.6H674.5z"/>
<path class="st1" d="M741.3,151.5V72.9h44.1v15.9h-25.8v14.7H779v15.4h-19.4v16.8h26.8v15.9L741.3,151.5L741.3,151.5z"/>
<path class="st1" d="M816.3,151.5V72.9h21.8c21.2,0,32.1,10.3,32.1,38.2c0,25.3-8.5,40.4-31.8,40.4H816.3z M839.4,135 c8.6,0,12-7.9,12-23.5c0-17.2-4.4-22-12.1-22H835V135H839.4z"/>
<path class="st1" d="M944.8,151.5V72.9h22.6c19.1,0,27.4,6.2,27.4,20.8c0,7.9-3.5,13.6-10.2,16.5v0.2c7.7,2.6,11.8,8.1,11.8,18 c0,18.8-12.9,23.1-28.9,23.1H944.8z M968.3,104.1c4.9,0,7.8-2.4,7.8-8.3c0-5.6-3.2-7.2-7.8-7.2h-5.6v15.5H968.3z M968.8,135.8 c6.7,0,9-2.7,9-8.9c0-6.7-3.6-8.9-8.9-8.9h-6.1v17.7L968.8,135.8L968.8,135.8z"/>
<path class="st1" d="M1036.6,151.5v-30.4l-19.5-48.2h19.3l9.3,27.9h0.2l9.9-27.9h19.1l-19.8,48.2v30.4H1036.6z"/>
</g>
<g>
<path class="st1" d="M492.2,363.3h121.3c15,0,15.6-9.7,15.6-15c0-7.5-3.7-13.8-16.9-17.8l-85.6-25.6 c-28.4-8.4-36.9-30.6-36.9-61.3c0-32.8,14.4-55.3,50.6-55.3h135v43.8H562.2c-8.1,0-15.3,3.1-15.3,14.4c0,8.4,4.4,14.4,17.5,18.4 l76.9,22.5c32.5,9.4,45.3,25.6,45.3,61.6c0,34.7-12.8,58.1-50,58.1H492.2V363.3z"/>
<path class="st1" d="M706.9,407.1v-185c0-23.4,12.8-33.8,33.1-33.8h116.3c48.4,0,59.1,30.3,59.1,74.1s-10.6,74.7-59.1,74.7 h-93.1v70H706.9z M827.3,293.3c23.4,0,30.6-9.1,30.6-30.9s-7.2-30.3-30.6-30.3h-49.4c-10.9,0-14.7,3.8-14.7,14.7v46.6 L827.3,293.3L827.3,293.3z"/>
<path class="st1" d="M1162.1,297.7c0,91.3-30.3,111.9-116.3,111.9S929.5,389,929.5,297.7c0-91,30.3-111.9,116.3-111.9 C1131.7,185.8,1162.1,206.7,1162.1,297.7z M1045.8,365.8c45.6,0,58.8-16.6,58.8-68.1s-13.1-68.1-58.8-68.1 c-45.6,0-58.8,16.6-58.8,68.1S1000.2,365.8,1045.8,365.8z"/>
<path class="st1" d="M1248,407.1v-175h-81.3v-43.8h218.8v43.8h-81.3v175H1248z"/>
</g>
</g>
<g>
<path class="st1" d="M1410.9,214.7v-21.5h-6.9v-4.9h19.7v4.9h-6.9v21.5H1410.9z M1448.6,214.7v-18.9h-0.1l-5.2,18.9h-5l-5.5-18.9 h-0.1v18.9h-5.5v-26.4h8.3l5.2,18.4h0.1l5.4-18.4h8v26.4H1448.6z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -5,45 +5,49 @@
* Also manages spots (devices) & messages
*/
class Feed extends PhpObject {
//Spot feed
const FEED_HOOK = 'https://api.findmespot.com/spot-main-web/consumer/rest-api/2.0/public/feed/';
const FEED_TYPE_XML = '/message.xml';
const FEED_TYPE_JSON = '/message.json';
const FEED_MAX_REFRESH = 5 * 60; //Seconds
//DB Tables
const SPOT_TABLE = 'spots';
const FEED_TABLE = 'feeds';
const MSG_TABLE = 'messages';
/**
* Database Handle
* @var Db
*/
private $oDb;
private $iFeedId;
private $sRefFeedId;
private $iLastUpdate;
public function __construct(Db &$oDb, $iFeedId=0) {
parent::__construct(__CLASS__, Settings::DEBUG);
$this->oDb = &$oDb;
if($iFeedId > 0) $this->setFeedId($iFeedId);
}
public function getFeedId() {
return $this->iFeedId;
}
public function getLastUpdate(): int {
return $this->iLastUpdate;
}
public function setFeedId($iFeedId) {
$this->iFeedId = $iFeedId;
$asFeed = $this->getFeed();
$this->sRefFeedId = $asFeed['ref_feed_id'];
$this->iLastUpdate = strtotime($asFeed['last_update']);
}
public function createFeedId($oProjectId) {
$this->setFeedId($this->oDb->insertRow(self::FEED_TABLE, array(
Db::getId(Project::PROJ_TABLE) => $oProjectId,
@@ -51,39 +55,39 @@ class Feed extends PhpObject {
)));
return $this->getFeedId();
}
public function setRefFeedId($sRefFeedId) {
return $this->updateField('ref_feed_id', $sRefFeedId);
}
public function setSpotId($iSpotId) {
return $this->updateField(Db::getId(self::SPOT_TABLE), $iSpotId);
}
public function setProjectId($iProjectId) {
return $this->updateField(Db::getId(Project::PROJ_TABLE), $iProjectId);
}
public function getSpots() {
$asSpots = $this->oDb->selectRows(array('from'=>self::SPOT_TABLE));
foreach($asSpots as &$asSpot) $asSpot['id'] = $asSpot[Db::getId(self::SPOT_TABLE)];
return $asSpots;
}
public function getFeeds($iFeedId=0) {
$asInfo = array('from'=>self::FEED_TABLE);
if($iFeedId > 0) $asInfo['constraint'] = array(Db::getId(self::FEED_TABLE)=>$iFeedId);
$asFeeds = $this->oDb->selectRows($asInfo);
foreach($asFeeds as &$asFeed) $asFeed['id'] = $asFeed[Db::getId(self::FEED_TABLE)];
return $asFeeds;
}
public function getFeed() {
$asFeeds = $this->getFeeds($this->getFeedId());
return array_shift($asFeeds);
}
public function getMessages($asActivePeriod = array()) {
$asInfo = array(
'select' => array('id_message', 'ref_msg_id', 'type', 'latitude', 'longitude', 'site_time', 'unix_time'),
@@ -92,7 +96,7 @@ class Feed extends PhpObject {
'constOpe' => array(Db::getId(self::FEED_TABLE) => "="),
'orderBy' => array('site_time'=>'ASC')
);
if(!empty($asActivePeriod)) {
$asInfo['constraint']['site_time'] = $asActivePeriod;
$asInfo['constOpe']['site_time'] = "BETWEEN";
@@ -100,22 +104,22 @@ class Feed extends PhpObject {
return $this->oDb->selectRows($asInfo);
}
public function checkUpdateFeed($sProjectMode) {
$bNewMsg = false;
//Spam Check: no more than 1 API request per 5 minutes
if($sProjectMode == Project::MODE_BLOG) {
$oLastUpdate = new DateTime('@'.$this->iLastUpdate);
$oNow = new DateTime('now');
$iSecDiff = $oNow->getTimestamp() - $oLastUpdate->getTimestamp();
if($iSecDiff > self::FEED_MAX_REFRESH) $bNewMsg = $this->updateFeed();
}
return $bNewMsg;
}
private function updateFeed() {
$bNewMsg = false;
$asData = $this->retrieveFeed();
@@ -123,14 +127,14 @@ class Feed extends PhpObject {
if(!isset($asData['response']['errors']) || empty($asData['response']['errors'])) {
$asMsgs = $asData['response']['feedMessageResponse']['messages'];
$asFeed = $asData['response']['feedMessageResponse']['feed'];
//Fix unstable Spot API Structure
if(array_key_exists('message', $asMsgs)) $asMsgs = $asMsgs['message']; //Sometimes adds an extra "message" level
if(!array_key_exists(0, $asMsgs)) $asMsgs = array($asMsgs); //Jumps a level when there is only 1 message
//Update Spot, Feed & Messages
if(!empty($asMsgs) && array_key_exists('messengerId', $asMsgs[0])) {
//Update Spot Info from the first message
$asSpotInfo = array(
'ref_spot_id' => $asMsgs[0]['messengerId'],
@@ -138,7 +142,7 @@ class Feed extends PhpObject {
'model' => $asMsgs[0]['modelId']
);
$iSpotId = $this->oDb->insertUpdateRow(self::SPOT_TABLE, $asSpotInfo, array('ref_spot_id'));
//Update Feed Info and last update date
$asFeedInfo = array(
'ref_feed_id' => $asFeed['id'],
@@ -149,7 +153,7 @@ class Feed extends PhpObject {
'last_update' => $sLastUpdate
);
$iFeedId = $this->oDb->insertUpdateRow(self::FEED_TABLE, $asFeedInfo, array('ref_feed_id'));
//Update Messages
foreach($asMsgs as $asMsg) {
$asMsg = array(
@@ -164,7 +168,7 @@ class Feed extends PhpObject {
'content' => $asMsg['messageContent'],
'battery_state' => $asMsg['batteryState']
);
$iMsgId = $this->oDb->selectId(self::MSG_TABLE, array('ref_msg_id'=>$asMsg['ref_msg_id']));
if(!$iMsgId) {
$this->oDb->insertRow(self::MSG_TABLE, $asMsg);
@@ -175,10 +179,10 @@ class Feed extends PhpObject {
}
}
else $this->oDb->updateRow(self::FEED_TABLE, $this->getFeedId(), array('last_update'=>$sLastUpdate));
return $bNewMsg;
}
private function retrieveFeed() {
$sContent = '[]';
if($this->sRefFeedId !='') {
@@ -187,14 +191,14 @@ class Feed extends PhpObject {
}
return json_decode($sContent, true);
}
private function updateField($sField, $oValue) {
$bResult = ($this->oDb->updateRow(self::FEED_TABLE, $this->getFeedId(), array($sField=>$oValue)) > 0);
$this->setFeedId($this->getFeedId());
return $bResult;
}
public function delete() {
$sDesc = '';
if($this->getFeedId() > 0) {
@@ -202,7 +206,7 @@ class Feed extends PhpObject {
if(!$bSuccess) $sDesc = $this->oDb->getLastError();
}
else $sDesc = 'Error while setting project: no Feed ID';
return $sDesc;
}
}
}

View File

@@ -1,39 +1,39 @@
<?php
class Project extends PhpObject {
//Spot Mode
const MODE_PREVIZ = 'P';
const MODE_BLOG = 'B';
const MODE_HISTO = 'H';
const MODES = array('previz'=>self::MODE_PREVIZ, 'blog'=>self::MODE_BLOG, 'histo'=>self::MODE_HISTO);
//DB Tables
const PROJ_TABLE = 'projects';
/**
* Database Handle
* @var Db
*/
private $oDb;
private $iProjectId;
private $sMode;
private $sName;
private $sCodeName;
private $asActive;
private $asGeo;
public function __construct(Db &$oDb, $iProjectId=0) {
parent::__construct(__CLASS__, Settings::DEBUG);
$this->oDb = &$oDb;
if($iProjectId > 0) $this->setProjectId($iProjectId);
}
public function getProjectId() {
return $this->iProjectId;
}
public function setProjectId($iProjectId=0) {
if($iProjectId > 0) {
$this->iProjectId = $iProjectId;
@@ -46,7 +46,7 @@ class Project extends PhpObject {
* Selected Project [-------Project 1-------][------------Project 2-------------][---------------Project 3------------------
* Mode --P--][--------B--------][--P--][-----------B---------------][---P---][-----B-----][---------H----------
*/
$sQuery =
$sQuery =
"SELECT MAX(id_project) ".
"FROM projects ".
"WHERE active_to = (".
@@ -61,39 +61,39 @@ class Project extends PhpObject {
$asResult = $this->oDb->getArrayQuery($sQuery, true);
$this->iProjectId = array_shift($asResult);
}
$this->setProjectInfo();
}
public function createProjectId() {
$this->setProjectId($this->oDb->insertRow(self::PROJ_TABLE, array('timezone'=>Settings::TIMEZONE)));
return $this->getProjectId();
}
public function getMode() {
return $this->sMode;
}
public function getProjectName() {
return $this->sName;
}
public function setProjectName($sName) {
return $this->updateField('name', $sName);
}
public function getProjectCodeName() {
return $this->sCodeName;
}
public function setProjectCodeName($sCodeName) {
return $this->updateField('codename', $sCodeName);
}
public function getActivePeriod($sFromTo='') {
return ($sFromTo=='')?$this->asActive:$this->asActive[$sFromTo];
}
public function setActivePeriod($oValue, $sFromTo='') {
if($sFromTo=='') {
$this->updateField('active_from', $oValue['from']);
@@ -103,15 +103,15 @@ class Project extends PhpObject {
return $this->updateField('active_'.$sFromTo, $oValue);
}
}
public function getTimeZone() {
return $this->asGeo['timezone'];
}
public function setTimeZone($sTimeZone) {
return $this->updateField('timezone', $sTimeZone);
}
public function getFeedIds() {
return $this->oDb->selectColumn(
Feed::FEED_TABLE,
@@ -119,7 +119,7 @@ class Project extends PhpObject {
array(Db::getId(self::PROJ_TABLE) => $this->getProjectId())
);
}
public function getProjects($iProjectId=0) {
$bSpecificProj = ($iProjectId > 0);
$asInfo = array(
@@ -135,7 +135,7 @@ class Project extends PhpObject {
'from' => self::PROJ_TABLE
);
if($bSpecificProj) $asInfo['constraint'] = array(Db::getId(self::PROJ_TABLE)=>$iProjectId);
$asProjects = $this->oDb->selectRows($asInfo, 'codename');
foreach($asProjects as $sCodeName=>&$asProject) {
switch($asProject['mode']) {
@@ -143,20 +143,31 @@ class Project extends PhpObject {
case 1: $asProject['mode'] = self::MODE_BLOG; break;
case 2: $asProject['mode'] = self::MODE_HISTO; break;
}
if($sCodeName!= '' && !Converter::isGeoJsonValid($sCodeName)) Converter::convertToGeoJson($sCodeName);
$asProject['geofilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, GeoJson::EXT));
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Geo::getFilePath($sCodeName, Gpx::EXT));
$asProject['codename'] = $sCodeName;
}
return $bSpecificProj?$asProject:$asProjects;
}
public function getProject() {
return $this->getProjects($this->getProjectId());
}
public function getLastUpdate(): int {
$iLastUpdate = INF;
$asFeedIds = $this->getFeedIds();
foreach($asFeedIds as $iFeedId) {
$iLastUpdate = min($iLastUpdate, (new Feed($this->oDb, $iFeedId))->getLastUpdate());
}
return $iLastUpdate;
}
private function setProjectInfo() {
if($this->getProjectId() > 0) {
$asProject = $this->getProject();
@@ -169,14 +180,14 @@ class Project extends PhpObject {
}
else $this->addError('Error while setting project: no project ID');
}
private function updateField($sField, $oValue) {
$bResult = ($this->oDb->updateRow(self::PROJ_TABLE, $this->getProjectId(), array($sField=>$oValue)) > 0);
$this->setProjectInfo();
return $bResult;
}
public function delete() {
$sDesc = '';
if($this->getProjectId() > 0) {
@@ -184,7 +195,7 @@ class Project extends PhpObject {
if(!$bSuccess) $sDesc = $this->oDb->getLastError();
}
else $sDesc = 'Error while setting project: no project ID';
return $sDesc;
}
}
}

View File

@@ -3,8 +3,8 @@
/* Timezones
* - Feeds (table `feeds`):
* - last_update: timestamp in site time (see Settings::TIMEZONE)
* - Spot Messages (table `messages`):
* - unix_time: UNIX (int) in UTC
* - Spot Messages (table `messages`):
* - unix_time: UNIX (int) in UTC
* - site_time: timestamp in site time (see Settings::TIMEZONE)
* - iso_time: raw ISO 8601 in local timezone
* - Medias (table `medias`):
@@ -18,30 +18,30 @@ class Spot extends Main
{
//Database
const POST_TABLE = 'posts';
const FEED_CHUNK_SIZE = 15;
const MAIL_CHUNK_SIZE = 5;
const DEFAULT_LANG = 'en';
/**
* Active Project
* @var Project
*/
private $oProject;
/**
* Media Class
* @var Media
*/
private $oMedia;
/**
* User
* @var User
*/
private $oUser;
public function __construct($oClassManagement, $sProcessPage, $sTimezone)
{
$asClasses = array(
@@ -53,22 +53,22 @@ class Spot extends Main
array('name'=>'email', 'project'=>true)
);
parent::__construct($oClassManagement, $sProcessPage, $asClasses, true, __FILE__, $sTimezone);
$this->oUser = new User($this->oDb);
$this->oClassManagement->incClass('translator');
$this->oLang = new Translator('', self::DEFAULT_LANG);
$this->oProject = new Project($this->oDb);
$this->oMedia = new Media($this->oDb, $this->oProject);
}
protected function install()
{
//Install DB
$this->oDb->install();
}
protected function getSqlOptions()
{
return array
@@ -129,7 +129,7 @@ class Spot extends Main
)
);
}
public function getMainPage($asGlobalVars = array(), $sMainPage = 'index', $asMainPageTags=array())
{
return parent::getMainPage(
@@ -146,7 +146,7 @@ class Spot extends Main
'default_timezone' => Settings::TIMEZONE
)
),
'index',
$sMainPage,
array(
'host_url' => $this->asContext['serv_name'],
'filepath_css' => self::addTimestampToFilePath('style/spot.css'),
@@ -159,35 +159,35 @@ class Spot extends Main
)
);
}
/* Managing projects */
public function setProjectId($iProjectId=0) {
$this->oProject->setProjectId($iProjectId);
}
public function updateProject() {
$bNewMsg = false;
//Update all feeds belonging to the project
$asFeeds = $this->oProject->getFeedIds();
foreach($asFeeds as $iFeedId) {
$oFeed = new Feed($this->oDb, $iFeedId);
$bNewMsg = $bNewMsg || $oFeed->checkUpdateFeed($this->oProject->getMode());
}
//Send Update Email
if($bNewMsg) {
$oEmail = new Email($this->asContext['serv_name'], 'email_update');
$oEmail->setDestInfo($this->oUser->getActiveUsersInfo());
//Add Position
$asMessages = $this->getSpotMessages();
$asLastMessage = end($asMessages);
$asLastMessage['token'] = Settings::GEO_SERVER_TOKEN;
$oEmail->oTemplate->setTags($asLastMessage);
$oEmail->oTemplate->setTag('date_time', 'time:'.$asLastMessage['unix_time'], 'd/m/Y, H:i');
//Add latest news feed
$asNews = $this->getNewsFeed(0, true);
$iPostCount = 0;
@@ -206,11 +206,11 @@ class Spot extends Main
}
if($iPostCount == self::MAIL_CHUNK_SIZE) break;
}
$oEmail->send();
}
}
public function genCronFile() {
//$bSuccess = (file_put_contents('spot_cron.sh', '#!/bin/bash'."\n".'cd '.dirname($_SERVER['SCRIPT_FILENAME'])."\n".'php -f index.php a=update_feed')!==false);
$sFileName = 'spot_cron.sh';
@@ -221,17 +221,17 @@ class Spot extends Main
$bSuccess = (file_put_contents($sFileName, $sContent)!==false);
return self::getJsonResult($bSuccess, '');
}
public function getMarkers()
{
$asMessages = $this->getSpotMessages();
$bSuccess = !empty($this->getMedias('posted_on') + $asMessages + $this->getPosts());
$sDesc = $bSuccess?'':self::NO_DATA;
//Add medias
if($bSuccess) {
$asMedias = $this->getMedias('taken_on');
//Assign medias to closest message
$iIndex = 0;
$iMaxIndex = count($asMessages) - 1;
@@ -245,47 +245,51 @@ class Spot extends Main
$iHalfWayPoint = ($asMessages[$iIndex - 1]['unix_time'] + $asMessages[$iIndex]['unix_time'])/2;
$iMsgIndex = ($asMedia['unix_time'] >= $iHalfWayPoint)?$iIndex:($iIndex - 1);
}
$asMessages[$iMsgIndex]['medias'][] = $asMedia;
}
}
return self::getJsonResult($bSuccess, $sDesc, $asMessages);
//Add Project Last Update
$asLastUpdate = array();
$this->addTimeStamp($asLastUpdate, $this->oProject->getLastUpdate());
return self::getJsonResult($bSuccess, $sDesc, array('messages' => $asMessages, 'last_update'=>$asLastUpdate));
}
public function subscribe($sEmail) {
$asResult = $this->oUser->addUser($sEmail, $this->oLang->getLanguage(), date_default_timezone_get());
$asUserInfo = $this->oUser->getUserInfo();
//Send Confirmation Email
if($asResult['result'] && $asResult['desc']=='lang:nl_subscribed') {
$oConfEmail = new Email($this->asContext['serv_name'], 'email_conf');
$oConfEmail->setDestInfo($asUserInfo);
$oConfEmail->send();
}
return self::getJsonResult($asResult['result'], $asResult['desc'], $asUserInfo);
}
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();
//Get messages from all feeds belonging to the project
$asFeeds = $this->oProject->getFeedIds();
foreach($asFeeds as $iFeedId) {
@@ -297,18 +301,21 @@ class Spot extends Main
$asMessage['longitude'] = floatval($asMessage['longitude']);
$asMessage['lat_dms'] = self::decToDms($asMessage['latitude'], 'lat');
$asMessage['lon_dms'] = self::decToDms($asMessage['longitude'], 'lon');
$this->addTimeStamp($asMessage, $asMessage['unix_time']);
}
}
//Sort chronologically
usort($asMessages, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
//Add Display ID
$asSortedMessages = array_values($asMessages);
foreach($asSortedMessages as $iIndex=>&$asSortedMessage) $asSortedMessage['displayed_id'] = $iIndex + 1;
return $asSortedMessages;
}
/**
* Get valid medias based on $sTimeRefField:
* - taken_on: Date/time on which the media was taken
@@ -325,20 +332,20 @@ class Spot extends Main
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']));
$this->addTimeStamp($asMedia, strtotime($sTimeRef));
$asValidMedias[] = $asMedia;
}
}
usort($asValidMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
$asSortedMedias = array_values($asValidMedias);
foreach($asSortedMedias as $iIndex=>&$asSortedMedia) $asSortedMedia['displayed_id'] = $iIndex + 1;
return $asSortedMedias;
}
private function getPosts()
{
$asInfo = array(
@@ -353,23 +360,23 @@ class Spot extends Main
$asPosts = $this->oDb->selectRows($asInfo);
foreach($asPosts as &$asPost) {
$iUnixTimeStamp = strtotime($asPost['site_time']); //assumes site timezone (Settings::TIMEZONE)
$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);
}
usort($asPosts, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
return $asPosts;
}
private function addTimeStamp(&$asData, $iTime) {
$asData['unix_time'] = (int) $iTime;
$asData['relative_time'] = Toolbox::getDateTimeDesc($iTime, $this->oLang->getLanguage());
$asData['formatted_time'] = $this->getTimeFormat($iTime);
}
public function getNewsFeed($iChunk=0, $bInternal=false)
{
$bHistoMode = ($this->oProject->getMode() == Project::MODE_HISTO);
@@ -395,7 +402,7 @@ class Spot extends Main
foreach($asFeedTypeInfo['feed'] as $asFeed) {
$sPriority = $asFeedTypeInfo['priority'];
if($bHistoMode) $sPriority = count($asFeedTypes) - 1 - $asFeedTypeInfo['priority'];
$iTableId = $asFeed[Db::getId($asFeedTypeInfo['table'])];
$iId = ($asFeed['unix_time'] * -1).'.'.$sPriority.$iTableId;
$asFeeds[$iId] = $asFeed;
@@ -403,52 +410,52 @@ class Spot extends Main
$asFeeds[$iId]['id'] = $iTableId;
}
}
//Sort by key
if($bHistoMode) krsort($asFeeds);
else ksort($asFeeds);
//Split chunks
$asFeeds = array_slice($asFeeds, $iChunk * self::FEED_CHUNK_SIZE, self::FEED_CHUNK_SIZE);
$asFeeds = array_slice($asFeeds, $iChunk * self::FEED_CHUNK_SIZE, self::FEED_CHUNK_SIZE);
return $bInternal?$asFeeds:self::getJsonResult(true, '', $asFeeds);
}
public function syncMedias() {
return $this->oMedia->syncFileFolder();
}
public function addPost($sName, $sPost)
{
$asData = array(
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
'name' => mb_strtolower(trim($sName)),
'name' => mb_strtolower(trim($sName)),
'content' => trim($sPost),
'site_time' => date(Db::TIMESTAMP_FORMAT) //site time (Settings::TIMEZONE)
);
if($this->oUser->getUserId() > 0) $asData[Db::getId(User::USER_TABLE)] = $this->oUser->getUserId();
$iPostId = $this->oDb->insertRow(self::POST_TABLE, $asData);
$this->oUser->updateNickname($sName);
return self::getJsonResult(($iPostId > 0), '');
}
public function upload()
{
$this->oClassManagement->incClass('uploader', true);
$oUploader = new Uploader($this->oMedia, $this->oLang);
return $oUploader->sBody;
}
public function addComment($iMediaId, $sComment) {
$oMedia = new Media($this->oDb, $this->oProject, $iMediaId);
$asResult = $oMedia->setComment($sComment);
return self::getJsonResult($asResult['result'], $asResult['desc'], $asResult['data']);
}
public function getAdminSettings() {
$oFeed = new Feed($this->oDb);
$asData = array(
@@ -458,12 +465,12 @@ class Spot extends Main
);
return self::getJsonResult(true, '', $asData);
}
public function setAdminSettings($sType, $iId, $sField, $sValue) {
$bSuccess = false;
$sDesc = '';
$asResult = array();
switch($sType) {
case 'project':
$oProject = new Project($this->oDb, $iId);
@@ -504,14 +511,14 @@ class Spot extends Main
break;
}
if(!$bSuccess) $sDesc = Mask::LANG_PREFIX.'error_commit_db';
return self::getJsonResult($bSuccess, $sDesc, array($sType=>array($asResult)));
}
public function delAdminSettings($sType, $iId) {
$bSuccess = false;
$sDesc = '';
switch($sType) {
case 'project':
$oProject = new Project($this->oDb, $iId);
@@ -523,17 +530,17 @@ class Spot extends Main
break;
}
$bSuccess = ($sDesc=='');
return self::getJsonResult($bSuccess, $sDesc, array($sType=>array(array('id'=>$iId, 'del'=>$bSuccess))));
}
public function createProject() {
$oProject = new Project($this->oDb);
$iNewProjectId = $oProject->createProjectId();
$oFeed = new Feed($this->oDb);
$oFeed->createFeedId($iNewProjectId);
return self::getJsonResult($iNewProjectId>0, '', array(
'project' => array($oProject->getProject()),
'feed' => array($oFeed->getFeed())
@@ -543,30 +550,30 @@ class Spot extends Main
public function convertGpxToGeojson($sGeoFileName) {
return Converter::convertToGeoJson($sGeoFileName);
}
public static function decToDms($dValue, $sType) {
if($sType=='lat') $sDirection = ($dValue >= 0)?'N':'S'; //Latitude
else $sDirection = ($dValue >= 0)?'E':'W'; //Longitude
$dLeft = abs($dValue);
//Degrees
$iDegree = floor($dLeft);
$dLeft -= $iDegree;
//Minutes
$iMinute = floor($dLeft * 60);
$dLeft -= $iMinute / 60;
//Seconds
$fSecond = round($dLeft * 3600, 1);
return $iDegree.'°'.$iMinute."'".$fSecond.'"'.$sDirection;
}
public function getTimeFormat($iTime) {
$sDate = date('d/m/Y', $iTime);
$sTime = date('H:i', $iTime);
return $this->oLang->getTranslation('date_time', array($sDate, $sTime));
}
}
}

View File

@@ -94,5 +94,3 @@ $sDebug = ob_get_clean();
if(Settings::DEBUG && $sDebug!='') $oSpot->addUncaughtError($sDebug);
echo $sResult;
?>

View File

@@ -28,6 +28,7 @@ map_ign_france = IGN (France)
map_ign_spain = IGN (Spain)
map_linz = LINZ (New Zealand)
map_usgs = USGS (USA)
map_natgeo = National Geographic (USA)
pic = Picture
pics = Pictures
@@ -56,7 +57,7 @@ ref_feed_id = Ref. Feed ID
spot_id = Spot ID
name = Name
status = Status
last_update = Last Update
last_update = Last Spot update
ref_spot_id = Ref. Spot ID
model = Model
delete = Delete
@@ -105,4 +106,4 @@ legend = Legend
credits_project = Spotty Project
credits_git = Git Repository
credits_license = under GPLv3 license
credits_license = under GPLv3 license

View File

@@ -28,6 +28,7 @@ map_ign_france = IGN (France)
map_ign_spain = IGN (Espagne)
map_linz = LINZ (Nouvelle-Zélande)
map_usgs = USGS (États-Unis)
map_natgeo = National Geographic (États-Unis)
pic = Photo
pics = Photos
@@ -56,7 +57,7 @@ ref_feed_id = ID Feed ref.
spot_id = ID Spot
name = Description
status = Statut
last_update = Dernière maj
last_update = Dernière maj depuis Spot
ref_spot_id = ID Spot ref.
model = Modèle
delete = Supprimer
@@ -105,4 +106,4 @@ legend = Légende
credits_project = Projet Spotty
credits_git = Dépôt Git
credits_license = sous licence GPLv3
credits_license = sous licence GPLv3

View File

@@ -5,7 +5,10 @@
<div id="map"></div>
<div id="settings">
<div id="settings-panel">
<div class="settings-header"><div class="logo"><img src="images/logo_black.png" /></div></div>
<div class="settings-header">
<div class="logo"><img src="images/logo_black.png" /></div>
<div id="last_update"><p><img src="images/spot-logo-only.svg" /><abbr></abbr></p></div>
</div>
<div class="settings-sections">
<div id="settings-sections-scrollbox">
<div class="settings-section">
@@ -66,7 +69,7 @@ oSpot.pageInit = function(asHash) {
oSpot.onResize = function() {
self.tmp('map_offset', -1 * (isFeedPanelOpen()?self.tmp('$Feed').outerWidth(true):0) / $('body').outerWidth(true));
if(typeof self.tmp('elev') != 'undefined') {
var bElevOpened = self.tmp('elev')._showState;
if(bElevOpened) self.tmp('elev')._expand();
@@ -95,7 +98,7 @@ function toggleFeedPanel(bShow, sMapAction) {
self.tmp('$Projects').toggleClass('with-feed', (typeof bShow === 'undefined')?null:bShow);
oSpot.onResize();
if(isMobile()) $('#settings-button').toggle(!isFeedPanelOpen());
sMapAction = sMapAction || 'panTo';
switch(sMapAction) {
case 'none': break;
@@ -113,7 +116,7 @@ function toggleSettingsPanel(bShow) {
self.tmp('$Projects').toggleClass('with-settings', (typeof bShow === 'undefined')?null:bShow);
oSpot.onResize();
if(isMobile()) $('#post-button').toggle(!isSettingsPanelOpen());
oSpot.tmp('map').panBy([(isSettingsPanelOpen()?-1:1)*self.tmp('$Settings').outerWidth(true)/2, 0], {duration: 0.5});
}
@@ -121,6 +124,13 @@ function isSettingsPanelOpen() {
return self.tmp('$Projects').hasClass('with-settings');
}
function updateSettingsPanel(asLastUpdate) {
var $LastUpdate = self.tmp('$Settings').find('#last_update').toggle(self.vars(['project', 'mode']) == self.consts.modes.blog);
$LastUpdate.find('abbr')
.attr('title', asLastUpdate.formatted_time)
.text(oSpot.lang('last_update')+' '+asLastUpdate.relative_time);
}
function isMobile() {
return $('#mobile').is(':visible');
}
@@ -139,9 +149,9 @@ function initPage(asHash) {
self.tmp('markers', 'object');
self.tmp('trail-markers', 'object');
self.tmp('marker_size', {width: 32, height: 32});
if(!isMobile()) toggleFeedPanel(true, 'none');
//Lightbox options
lightbox.option({
alwaysShowNavOnTouchDevices: true,
@@ -152,7 +162,7 @@ function initPage(asHash) {
resizeDuration: 600,
hasVideo: true
});
//Assign Track Type Colors
self.tmp('track-type-styles', 'object');
$('#legend').find('.line').each(function(iKey, oLegend){
@@ -160,7 +170,7 @@ function initPage(asHash) {
var sTrackType = $Legend.attr('class').replace('line', '').trim();
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]));
@@ -173,10 +183,10 @@ function initPage(asHash) {
$("#feed").onSwipe(function(aiDelta){
if(aiDelta.x > self.tmp('$Feed').outerWidth(true)/3 && aiDelta.x > Math.abs(aiDelta.y)) toggleFeedPanel(false);
});
//Feed Panel
initPosts();
//Settings Panel
initSettings();
@@ -189,19 +199,23 @@ function initPage(asHash) {
function initProject(sProjectCodeName, oFocusPost){
self.tmp('first_exec', false);
self.vars('project', self.vars(['projects', sProjectCodeName]));
//Page Title
self.setPageTitle(oSpot.vars(['project', 'name']));
//Timezone difference notice
var bSameTime = ((new Date()).toLocaleString([], {timeZone: oSpot.consts.timezone}) == (new Date()).toLocaleString([], {timeZone: oSpot.vars(['project', 'timezone'])}));
var bSameTime = (
(new Date()).toLocaleString([], {timeZone: oSpot.consts.timezone}) ==
(new Date()).toLocaleString([], {timeZone: oSpot.vars(['project', 'timezone'])})
);
self.tmp('site_tz_notice', bSameTime?'':getTimeZoneDesc(oSpot.consts.timezone));
self.tmp('proj_tz_notice', bSameTime?'':getTimeZoneDesc(self.vars(['project', 'timezone'])));
//Load Track & Markers
$.when(
//Markers: Spot Messages & Medias
self.get(
'markers',
'markers',
function(){},
{project_id: self.vars(['project', 'id'])},
function(e){console.log(e);}
@@ -213,13 +227,14 @@ function initProject(sProjectCodeName, oFocusPost){
mimeType: 'application/json'
})
).done(function(aoMessages, aoTracks) {
initSpotMessages(aoMessages[0]['data'] || [], aoTracks[0], aoMessages[0]['desc']=='No Data');
initSpotMessages(aoMessages[0]['data']['messages'] || [], aoTracks[0], aoMessages[0]['desc']=='No Data');
updateSettingsPanel(aoMessages[0]['data']['last_update']);
});
//Show/Hide Poster Panel
var bHistoMode = (self.vars(['project', 'mode']) == self.consts.modes.histo);
$('#poster').toggle(!bHistoMode);
//Feed auto-update
updateFeed(true, false, function(){focusOnPost(oFocusPost);});
self.tmp('simple-bar').getScrollElement().scrollTop = 0;
@@ -237,7 +252,7 @@ function initPosts() {
//Auto-adjust text area height
autosize($('#post'));
//Add post Event handling
$('#submit').click(function(){
if($('#poster').checkForm())
@@ -262,7 +277,7 @@ function initPosts() {
function initSettings(){
//Scrollbar
new SimpleBar($('#settings-sections-scrollbox')[0]);
//Feedback display function
var settingsFeedback = function(sType, sMsg){
$('<p>', {'class': sType})
@@ -273,7 +288,7 @@ function initSettings(){
.delay(5000)
.slideUp('fast', function(){$(this).remove();});
};
//Newsletter Subscription
$('#nl_btn').click(function(){
var sAction = $(this).prop('name');
@@ -282,7 +297,7 @@ function initSettings(){
if(!regexEmail.test(sEmail)) settingsFeedback('error', oSpot.lang('nl_invalid_email'));
else {
oSpot.get(
sAction,
sAction,
function(asData, sDesc) {
settingsFeedback('success', sDesc);
oSpot.vars('user', asData);
@@ -299,7 +314,7 @@ function initSettings(){
);
}
});
//Twink interface with user data
setUserInterface();
}
@@ -310,7 +325,7 @@ function setUserInterface() {
$('#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);
}
@@ -333,12 +348,12 @@ function getElevWidth() {
iSettingsPanelWidth = isSettingsPanelOpen()?self.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;
}
function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
//Tile layers
aoLayers = {};
aoLayers[oSpot.lang('map_satellite')] = L.tileLayer(self.tmp('tile_api'), {id: 'mapbox.satellite-streets', minZoom: 0, maxZoom: 19});
@@ -347,7 +362,8 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
aoLayers[oSpot.lang('map_ign_spain')] = L.tileLayer(self.tmp('tile_api'), {id: 'ign.es', minZoom: 1, maxZoom: 20});
aoLayers[oSpot.lang('map_linz')] = L.tileLayer(self.tmp('tile_api'), {id: 'linz', maxZoom: 17, continuousWorld: true, attribution: 'Sourced from LINZ. CC BY 4.0'});
aoLayers[oSpot.lang('map_usgs')] = L.tileLayer(self.tmp('tile_api'), {id: 'usgs', minZoom: 1, maxZoom: 16});
//aoLayers[oSpot.lang('map_natgeo')] = L.tileLayer(self.tmp('tile_api'), {id: 'natgeo.pct', minZoom: 5, maxZoom: 14});
//Map
var oMap = L.map(self.tmp('$Map')[0], {
layers: [aoLayers[oSpot.lang('map_satellite')]],
@@ -355,26 +371,26 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
zoomControl: false
});
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();
$PostButton.click(toggleFeedPanel);
oFeedPanel.onAdd = function(oMap) {return $PostButton[0];};
oFeedPanel.addTo(oMap);
//Controls: Legend
var oLegend = L.control({position: 'bottomleft'});
oLegend.onAdd = function(oMap) {return $('#legend').clone()[0];};
oLegend.addTo(oMap);
//Controls: Projects
var $Labels = $('<div>', {'class': 'leaflet-control-layers-base'});
$.each(self.vars('projects'), function(sCodeName, asProject){
@@ -384,7 +400,7 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
toggleSettingsPanel(false);
self.setHash(self.vars('page'), [$(this).val()]);
});
var $Label = $('<label>').append($('<div>')
.append($Radio)
.append($('<span>').text(' '+asProject.name))
@@ -393,10 +409,10 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
$Labels.append($Label);
});
$('#settings-projects').empty().append($Labels);
//Controls: Scale
oScale = L.control.scale({imperial: false, 'position':'bottomright'}).addTo(oMap);
//Controls: Elevation
if(!isMobile()) {
var aoElevTracks = {type: 'FeatureCollection', features:[], properties: {summary: 'Elevation'}};
@@ -408,7 +424,7 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
var sType = aoTracks.features[i].properties.type;
oTrack.properties.attributeType = sType;
aoElevTracks.features.push(oTrack);
//Legend
aoLegend.Elevation[sType] = {text: oSpot.lang('track_'+sType), color: self.tmp(['track-type-styles', sType, 'color'])};
}
@@ -446,18 +462,18 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
self.tmp('elev', oElev);
self.tmp('elev-data', [aoElevTracks]);
}
//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'));
//Actual Tracks: Track with corresponding colors
var oActualTracks = L.geoJson(aoTracks, {
style: function(oTrack) {
return self.tmp(['track-type-styles', oTrack.properties.type]);
}
}).addTo(oMap);
//"Hover" Tracks: Wider track (thus hover area) to avoid flickering popups
self.tmp('track', L.geoJson(aoTracks, {
style: {weight: 20, opacity: 0},
@@ -466,22 +482,22 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
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);
var aiCoords = feature.geometry.coordinates;
if(aiCoords && asProperties.type != 'hitchhiking') {
var iDistance = 0, iElevDrop = 0, iElevGain = 0, iTime = 0;
//Track duration (in hours)
for (var i = 1; i < aiCoords.length; i++) {
var oCurrPoint = new L.LatLng(aiCoords[i][1], aiCoords[i][0]);
var oPrevPoint = new L.LatLng(aiCoords[i - 1][1], aiCoords[i - 1][0]);
var iElevDelta = aiCoords[i][2] - aiCoords[i - 1][2];
var iSegDistance = oCurrPoint.distanceTo(oPrevPoint);
iDistance += iSegDistance;
iElevDrop += Math.min(iElevDelta, 0);
iElevGain += Math.max(iElevDelta, 0);
var iSpeedCorrecRatio = 0;
var iAngle = iElevDelta / iSegDistance;
if(iAngle < -1) iSpeedCorrecRatio = 0.5;
@@ -493,7 +509,7 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
else iSpeedCorrecRatio = 0.25;
iTime += iSegDistance / 1000 * iSpeedCorrecRatio / 3.5 ; //default speed: 3.5km/h
}
//Conversion of hours into natural language
var iTimeMinutes = 0, iTimeHours = 0, iTimeDays = Math.floor(iTime/8); //8 hours a day
if(iTimeDays > 1) iTimeDays = Math.round(iTimeDays * 2) / 2; //Round down to the closest half day
@@ -501,14 +517,14 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
iTimeDays = 0;
iTimeHours = Math.floor(iTime);
iTime -= iTimeHours;
iTimeMinutes = Math.floor(iTime * 4) * 15; //Round down to the closest 15 minutes
}
var sDuration = '~ '
+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+oSpot.lang(iTimeDays>1?'unit_days':'unit_day')):'')//Days
+((iTimeHours>0 || iTimeDays==0)?iTimeHours+oSpot.lang('unit_hour'):'') //Hours
+((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes) //Minutes
//Tooltip info
$('<p>', {'class':'detail'}).addIcon('fa-distance fa-fw', true).append(Math.round(iDistance/1000)+'km').appendTo($Tooltip);
$('<p>', {'class':'detail'}).addIcon('fa-time fa-fw', true).append(sDuration).appendTo($Tooltip);
@@ -520,24 +536,24 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
'start' : L.marker(new L.latLng(aiCoords[0][1], aiCoords[0][0]), {icon: getDivIcon('track-start')}),
'end' : L.marker(new L.latLng(aiCoords[aiCoords.length - 1][1], aiCoords[aiCoords.length - 1][0]), {icon: getDivIcon('track-end')})
});
oLayer
.bindPopup($Tooltip[0])
.on('mouseover click', function(e) {
var oLatLng = L.GeometryUtil.closest(self.tmp('map'), e.sourceTarget, e.latlng);
e.target.getPopup().setLatLng(oLatLng).openOn(self.tmp('map'));
var asTrailMarkers = self.tmp(['trail-markers', e.target.feature.properties.name]);
var oPointStart = self.tmp('map').latLngToLayerPoint(asTrailMarkers.start.getLatLng());
var oPointEnd = self.tmp('map').latLngToLayerPoint(asTrailMarkers.end.getLatLng());
if(oPointStart.distanceTo(oPointEnd) > 200) {
asTrailMarkers.start.addTo(self.tmp('map'));
asTrailMarkers.end.addTo(self.tmp('map'));
asTrailMarkers.end.addTo(self.tmp('map'));
}
})
.on('mouseout', function(e) {
e.target.closePopup();
e.target.closePopup();
var asTrailMarkers = self.tmp(['trail-markers', e.target.feature.properties.name]);
asTrailMarkers.start.remove();
asTrailMarkers.end.remove();
@@ -545,7 +561,7 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
}
}
}).addTo(oMap));
//Centering map
var bWithFeedPanel = (!bNoFeed && !isMobile());
if(self.vars(['project', 'mode'])==self.consts.modes.blog && aoMessages.length > 0)
@@ -556,17 +572,17 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
oMap.panBy([(bWithFeedPanel?1:0)*self.tmp('$Feed').outerWidth(true)/2, 0]);
}
else oMap.fitBounds(self.tmp('track').getBounds(), {paddingTopLeft: L.point(5, self.tmp('marker_size').height + 5), paddingBottomRight: L.point(5 + parseInt(bWithFeedPanel?self.tmp('$Feed').outerWidth(true):0), 5)});
//Spot Messages
$.each(aoMessages, function(iKey, oMsg){
//Marker
var oMarker = L.marker(L.latLng(oMsg.latitude, oMsg.longitude), {
id: oMsg.id_message,
riseOnHover: true,
icon: getDivIcon('message-in fa-rotate-270')
}).addTo(oMap);
//Tooltip
$Tooltip = $('<div>', {'class':'info-window'})
.append($('<h1>')
@@ -578,14 +594,14 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
.append($('<p>', {'class':'time'})
.addIcon('fa-time fa-fw fa-lg')
.append(oMsg.formatted_time+(self.vars(['project', 'mode'])==self.consts.modes.blog?' ('+oMsg.relative_time+')':'')));
//Tooltip: Time Zone
if(self.tmp('site_tz_notice')!='') {
$Tooltip.append($('<p>', {'class':'timezone'})
.addIcon('fa-timezone fa-fw fa-lg')
.append(self.tmp('site_tz_notice')));
}
//Tooltip: Medias
if(oMsg.medias) {
var $Medias = $('<div>', {'class':'medias'});
@@ -596,28 +612,71 @@ function initSpotMessages(aoMessages, aoTracks, bNoFeed) {
.append($('<p>').addIcon('fa-media fa-fw fa-lg').append(oSpot.lang('pics')))
.append($Medias);
}
oMarker.bindPopup($Tooltip[0], {
maxWidth: self.tmp('$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) && isMobile()) oMarker.openPopup();
oSpot.tmp(['markers', oMsg.id_message], oMarker);
});
/*
oSpot.tmp('tracks', aoTracks);
next(24, 0, 0, 5);
*/
}
/*
function next(iCurrTrack, iCurrIndex, iCurrOffset, iCurrZoom) {
var aoTracks = oSpot.tmp('tracks');
var aoOffset = {0:[0,0], 1:[-1,0], 2:[-1,1], 3:[0,1], 4:[1,1], 5:[1,0], 6:[1,-1], 7:[0,-1], 8:[-1,-1]};
console.log('Getting Track '+iCurrTrack+'/'+(aoTracks.features.length - 1)+', '+
'Point '+iCurrIndex+'/'+(aoTracks.features[iCurrTrack].geometry.coordinates.length - 1)+', '+
'Zoom '+iCurrZoom+'/14, '+
'Offset ['+aoOffset[iCurrOffset][0]+','+aoOffset[iCurrOffset][1]+']');
//Position map
var iLat = aoTracks.features[iCurrTrack].geometry.coordinates[iCurrIndex][1] + aoOffset[iCurrOffset][1] * 0.0347910214271;
var iLng = aoTracks.features[iCurrTrack].geometry.coordinates[iCurrIndex][0] + aoOffset[iCurrOffset][0] * 0.1016235351560;
oSpot.tmp('map').setView(L.latLng(iLat, iLng), iCurrZoom);
//Go to next
iCurrOffset++;
if(iCurrZoom < 13 || iCurrOffset > 8) {
iCurrOffset = 0;
iCurrZoom++;
if(iCurrZoom > 14) {
iCurrZoom = 5;
iCurrIndex += 100;
if(!(iCurrIndex in aoTracks.features[iCurrTrack].geometry.coordinates)) {
iCurrIndex = 0;
iCurrTrack++;
if(!(iCurrTrack in aoTracks.features)) return true;
}
}
}
timer = setTimeout(function(){next(iCurrTrack, iCurrIndex, iCurrOffset, iCurrZoom);}, 1000);
}
function stop() {
clearTimeout(timer);
}
*/
function getDivIcon(sIcon) {
return L.divIcon({
className: '',
html: '<span class="fa-stack"><i class="fa fa-message fa-stack-2x"></i><i class="fa fa-'+sIcon+' fa-stack-1x"></i></span>',
iconSize: [self.tmp('marker_size').width, self.tmp('marker_size').height],
iconAnchor: [self.tmp('marker_size').width / 2, self.tmp('marker_size').height] //position from icon's top left corner (iconAnchor = marker's position)
});
});
}
function onFeedScroll() {
@@ -630,35 +689,35 @@ function updateFeed(bFirstChunk, bDiscrete, fCallback) {
bFirstChunk = bFirstChunk || false;
bDiscrete = bDiscrete || false;
fCallback = fCallback || function(){};
if(self.tmp('updatable')) {
if(!self.tmp('out-of-data') || bFirstChunk) {
self.tmp('updatable', false);
if(!bDiscrete) $('#loading').show();
var $Posts = $('<div>');
if(bFirstChunk===true) {
self.tmp('news_chunk', 0);
}
self.get(
'feed',
function(asData) {
$('#loading').hide();
$.each(asData, function(iKey, asPost){
$Posts.append(getPost(asPost));
});
self.tmp('news_chunk', self.tmp('news_chunk') + 1);
self.tmp('out-of-data', Object.keys(asData).length != self.vars('chunk_size'));
if(bFirstChunk===true) self.tmp('$PostList').empty();
self.tmp('$PostList').append($Posts.children());
self.tmp('$PostList').find('img').waitForImages(true).done(fCallback);
self.tmp('updatable', true);
}, {
'project_id': self.vars(['project', 'id']),
@@ -681,7 +740,7 @@ function focusOnPost(oFocusPost) {
}
else if(!self.tmp('out-of-data')) updateFeed(false, false, function() {focusOnPost(oFocusPost);});
else console.log('Missing element ID '+sElemId);
//Reset Hash
var asHash = self.getHash();
self.setHash(asHash.page, [asHash.items[0]]);
@@ -696,15 +755,15 @@ function setFeedUpdateTimer(iSeconds, fCallback) {
function getPost(asPost) {
asPost.headerless = asPost.headerless || false;
var $Post = $('<div>', {'class':'post-item '+asPost.type+(asPost.headerless?' headerless':'')});
if(asPost.id) $Post.prop('id', asPost.type+'-'+asPost.id);
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 = {};
switch(asPost.type) {
case 'message':
$Body = $('<div>', {'class':'body-box'})
@@ -729,7 +788,7 @@ function getPost(asPost) {
var iRatio = -1 * iOffset / $('body').outerWidth(true);
self.tmp('map').setOffsetView(iRatio, oMarker.getLatLng(), 15);
}
if(!oMarker.isPopupOpen()) oMarker.openPopup();
})
)
@@ -763,15 +822,15 @@ function getPost(asPost) {
$Body = $('<p>', {'class':'flicker'}).addIcon('fa-post');
break;
}
$Post
.append($('<div>', {'class':'header'})
.append($('<span>', {'class':'index'}).addIcon('fa-'+sType))
.append($('<span>', {'class':'time'}).hoverSwap(sRelTime, sAbsTime+((self.tmp('site_tz_notice')!='')?' ('+self.tmp('site_tz_notice')+')':''))))
.append($('<div>', {'class':'body'}).append($Body));
if(asPost.displayed_id) $Post.find('.index').append(' '+oSpot.lang('counter', asPost.displayed_id));
return $Post;
}
@@ -781,24 +840,24 @@ function getWmtsApiUrl(sMapId, iLat, iLng, iZoom) {
function getMediaLink(asData, sType) {
var bVideo = (asData.subtype == 'video');
var $PostedOn =
var $PostedOn =
$('<span>', {'class': 'lb-caption-line', title: oSpot.lang(bVideo?'video':'pic')+' '+oSpot.lang('add_on', asData.posted_on_formatted)})
.addIcon('fa-upload fa-lg fa-fw', true)
.append(asData.posted_on_formatted);
var $TakenOn = (asData.taken_on == '0000-00-00 00:00:00')?'':
$('<span>', {'class': 'lb-caption-line', title: oSpot.lang(bVideo?'video':'pic')+' '+oSpot.lang(bVideo?'video_taken':'pic_taken', asData.taken_on_formatted)})
.addIcon('fa-'+asData.subtype+'-shot fa-lg fa-fw', true)
.append(asData.taken_on_formatted);
var $Timezone = (self.tmp('site_tz_notice') == '')?'':
$('<span>', {'class': 'lb-caption-line'})
.addIcon('fa-timezone fa-lg fa-fw', true)
.append(self.tmp('site_tz_notice'));
var $Title = $('<div>').append(sType=='marker'?$TakenOn:$PostedOn).append(sType=='marker'?$PostedOn:$TakenOn).append($Timezone);
var $Link =
var $Link =
$('<a>', {
'class': 'drill',
'href': asData.media_path,
@@ -809,7 +868,7 @@ function getMediaLink(asData, sType) {
})
.append($('<img>', {'src': asData.thumb_path, title: oSpot.lang(bVideo?'click_watch':'click_zoom')}))
.append($('<span>', {'class': 'drill-icon'}).addIcon('fa-drill-'+(bVideo?'video':'picture')));
return $Link;
}
@@ -821,4 +880,4 @@ function getGoogleMapsLink(asInfo) {
rel: 'noreferrer'
}).text(asInfo.lat_dms+' '+asInfo.lon_dms);
}
</script>
</script>

View File

@@ -12,30 +12,34 @@ $message-bg: #6DFF58;
$media-color: #333; //#635C28;
$media-bg: rgba(255,255,255,.8); //#F3EC9F;
//Legend colors
//Settings colors
$title-color: $post-color;
$subtitle-color: #999;
//Legend colors
$track-main-color: #00ff78;
$track-off-track-color: #0000ff;
$track-hitchhiking-color: #FF7814;
$legend-color: $post-color;
#projects {
&.with-feed {
#submap {
width: calc(100% - #{$panel-width});
min-width: calc(100% - #{$panel-width-max});
}
.leaflet-right {
width: $panel-width;
max-width: calc(#{$panel-width-max});
}
#feed {
z-index: 999;
transition: none;
}
#post-button {
.fa {
@extend .fa-next;
@@ -52,17 +56,17 @@ $legend-color: $post-color;
width: calc(100% - #{$panel-width});
min-width: calc(100% - #{$panel-width-max});
}
.leaflet-left {
width: $panel-width;
max-width: calc(#{$panel-width-max});
}
#settings {
z-index: 999;
transition: none;
}
#settings-button {
.fa {
@extend .fa-prev;
@@ -87,7 +91,7 @@ $legend-color: $post-color;
top: 0;
bottom: 0;
width: 100%;
.loader {
position: absolute;
font-size: 3em;
@@ -96,18 +100,18 @@ $legend-color: $post-color;
color: #CCC;
}
}
#map {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 100%;
.track_tooltip {
p {
margin: 0;
&.name {
font-weight: bold;
font-size: 1.2em;
@@ -132,14 +136,14 @@ $legend-color: $post-color;
border: none;
margin: $block-spacing;
box-shadow: 0 1px 7px rgba(0, 0, 0, .4);
&+ .leaflet-control {
margin-top: 0;
}
&.leaflet-control-scale {
padding: 0.5em;
.leaflet-control-scale-line {
background: none;
}
@@ -150,7 +154,7 @@ $legend-color: $post-color;
.leaflet-right, .leaflet-left {
transition: all 0.5s;
width: 0;
max-width: 0;
max-width: 0;
}
.leaflet-right .leaflet-control {
left: -100%;
@@ -172,8 +176,8 @@ $legend-color: $post-color;
height: 4px;
display: inline-block;
border-radius: 2px;
vertical-align: middle;
vertical-align: middle;
&.main {
background-color: $track-main-color;
}
@@ -184,7 +188,7 @@ $legend-color: $post-color;
background-color: $track-hitchhiking-color;
}
}
.desc {
font-size: 1em;
margin-left: 0.5em;
@@ -207,14 +211,14 @@ $legend-color: $post-color;
overflow: hidden;
text-decoration: none;
display: inline-block;
.drill-icon {
position: absolute;
display: inline-block;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
i {
transition: color 0.3s;
cursor: pointer;
@@ -222,7 +226,7 @@ $legend-color: $post-color;
}
}
.fa-stack {
.fa-stack {
.fa-message {
font-size: 32px;
text-shadow: rgba(0, 0, 0, 0.5) 3px 3px 3px;
@@ -244,7 +248,7 @@ $legend-color: $post-color;
}
/* Feed/Settings Panel */
#feed, #settings {
position: absolute;
top: 0;
@@ -254,19 +258,19 @@ $legend-color: $post-color;
transition-duration: 0.1s;
transition-delay: 0.5s;
overflow: hidden;
input, textarea {
background-color: $post-input-bg;
color: $post-color;
}
button {
background-color: $post-color;
color: $post-bg;
&:hover {
background-color: $post-input-bg;
color: $post-color;
color: $post-color;
}
}
}
@@ -274,7 +278,7 @@ $legend-color: $post-color;
right: 0;
width: #{$panel-width};
max-width: calc(#{$panel-width-max});
#posts {
position: absolute;
transition: all 0.5s;
@@ -282,33 +286,33 @@ $legend-color: $post-color;
bottom: 0;
right: 0;
width: 100%;
#posts_list {
position: relative;
}
#poster {
textarea#post {
margin-bottom: 1em;
width: calc(100% - 2em);
}
input#name {
width: calc(100% - 6em);
}
button#submit {
margin-left: 1em;
margin-bottom: 0.5em;
}
}
.body-box {
position:relative;
display: flex;
flex-direction: column;
}
.post-item {
margin-bottom: $block-spacing;
background: $post-bg;
@@ -316,11 +320,11 @@ $legend-color: $post-color;
border-radius: 3px;
width: calc(100% - #{$block-spacing});
box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.5);
&:first-child {
margin-top: $block-spacing;
}
.message {
margin: 0;
}
@@ -338,13 +342,13 @@ $legend-color: $post-color;
cursor: default;
font-size: 0.8em;
text-overflow: ellipsis;
overflow: hidden;
overflow: visible;
white-space: nowrap;
&.index {
width: 25%;
}
&.time {
width: 75%;
text-align: right;
@@ -356,7 +360,7 @@ $legend-color: $post-color;
clear: both;
padding: 0em 1em 1em;
}
&.headerless {
.header {
display: none;
@@ -369,30 +373,30 @@ $legend-color: $post-color;
&.message {
background: $message-bg;
color: $message-color;
p {
font-size: 0.9em;
margin: 0 0 .5em 0;
display: inline-block;
width: 100%;
}
a {
color: $message-color;
}
a.drill {
line-height: 0;
.drill-icon {
transform: translate(-16px, -32px);
.fa-message-in {
top: -1px;
left: -1px;
}
}
&:hover {
.fa-message {
@extend .#{$fa-css-prefix}-drill-message;
@@ -404,24 +408,24 @@ $legend-color: $post-color;
}
}
}
.staticmap {
width: 100%;
border-radius: 3px;
cursor: pointer;
}
}
&.post {
.body {
padding: 0em 1em 0.5em;
}
}
&.media {
background: $media-bg;
color: $media-color;
.body {
a {
display: inline-block;
@@ -430,7 +434,7 @@ $legend-color: $post-color;
color: $media-color;
position: relative;
line-height: 0;
&.drill {
&:hover {
.drill-icon .fa-drill-picture, .drill-icon .fa-drill-video {
@@ -440,10 +444,10 @@ $legend-color: $post-color;
opacity: 0;
}
}
.drill-icon {
font-size: 3em;
.fa-drill-picture {
color: transparent;
}
@@ -452,14 +456,14 @@ $legend-color: $post-color;
}
}
}
img {
width: 100%;
image-orientation: from-image;
outline: none;
border-radius: 3px;
}
.comment {
position: absolute;
left: 0;
@@ -478,11 +482,11 @@ $legend-color: $post-color;
}
}
}
&.loading {
.body {
text-align: center;
p {
display: inline-block;
font-size: 2em;
@@ -497,7 +501,7 @@ $legend-color: $post-color;
left: 0;
width: calc(#{$panel-width} + 3px); //Add box-shadow
max-width: calc(#{$panel-width-max} + 3px); //Add box-shadow
#settings-panel {
width: calc(100% - #{$block-spacing} - 3px); //Remove box-shadow
margin: $block-spacing;
@@ -513,24 +517,49 @@ $legend-color: $post-color;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
.settings-header {
text-align: center;
flex: 0 1 auto;
.logo {
background: rgba(255, 255, 255, .4);
padding: 1.5rem;
padding: 2rem 1rem;
border-radius: 3px 3px 0 0;
img {
width: 100%;
max-width: 180px;
transform: translateX(-10%); //Center Text, not logo. logo width (40px) / image width (200px) = 20%. And centering: 20% / 2 = 10%
}
}
#last_update {
margin-top: -2em;
padding: 0 1rem;
p {
text-align: center;
font-size: 0.8em;
margin: 0;
color: $subtitle-color;
cursor: pointer;
transform: translateX(calc(-0.5 * (12px + 0.5em))); //icon width + margin right
img {
width: 12px;
vertical-align: middle;
margin-right: 0.5em;
}
abbr {
text-decoration: none;
vertical-align: middle;
}
}
}
}
.settings-footer {
flex: 0 1 auto;
background: rgba(255, 255, 255, .4);
@@ -539,45 +568,46 @@ $legend-color: $post-color;
padding: 0.3rem;
text-align: center;
color: #888;
a {
color: #777;
text-decoration: none;
}
}
.settings-sections {
flex: 1 1 auto;
overflow: auto;
#settings-sections-scrollbox {
height: 100%;
width: 100%;
}
.settings-section {
display: inline-block;
margin: 1.5rem 1rem 0 1rem;
width: calc(100% - 2 * #{$block-spacing});
&:last-child {
margin-bottom: 1.5rem;
}
h1 {
margin: 0 0 $block-spacing;
color: $title-color;
}
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);
@@ -586,7 +616,7 @@ $legend-color: $post-color;
button#nl_btn {
margin-left: 1em;
margin-bottom: 1em;
&.subscribe .fa {
@extend .fa-send;
}
@@ -602,11 +632,11 @@ $legend-color: $post-color;
}
}
}
#settings-projects {
a.fa-download {
color: $legend-color;
&:hover {
color: #0078A8;
}
@@ -624,55 +654,55 @@ $legend-color: $post-color;
h1 {
font-size: 1.2em;
margin: 1em 0 1.2em;
i {
margin-right: 0.3125em;
}
}
p {
font-size: 1.0em;
margin: 0.5em 0 0 0;
i {
padding-right: 0.5em;
}
a {
color: $post-color;
}
}
.medias {
margin-top: -0.5rem;
line-height: 0;
a {
display: inline-block;
margin-right: $block-spacing;
margin-top: $block-spacing;
&:last-child {
margin-right: 0;
}
&.drill {
font-size: 2em;
.fa-drill-picture {
color: transparent;
}
.fa-drill-video {
color: rgba(255, 255, 255, 0.5);
}
&:hover {
.fa-drill-video, .fa-drill-picture {
color: rgba(255, 255, 255, 0.75);
}
}
}
img {
max-width: 200px;
max-height: 100px;
@@ -688,4 +718,4 @@ $legend-color: $post-color;
#elems {
display: none;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long