Adding Last Update
This commit is contained in:
17
images/spot-logo-only.svg
Normal file
17
images/spot-logo-only.svg
Normal 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
48
images/spot-logo.svg
Normal 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 |
74
inc/feed.php
74
inc/feed.php
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
175
inc/spot.php
175
inc/spot.php
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,5 +94,3 @@ $sDebug = ob_get_clean();
|
||||
if(Settings::DEBUG && $sDebug!='') $oSpot->addUncaughtError($sDebug);
|
||||
|
||||
echo $sResult;
|
||||
|
||||
?>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user