Merge pull request #557 from exodus4d/develop

v1.3.1
This commit is contained in:
Mark Friedrich
2017-12-11 21:08:19 +01:00
committed by GitHub
190 changed files with 3503 additions and 3399 deletions

View File

@@ -1,54 +0,0 @@
<?php
namespace App;
class Cortex extends Controller
{
function get()
{
$f3 = \Base::instance();
$f3->set('AUTOLOAD', $f3->get('AUTOLOAD').';app/cortex/');
$f3->set('QUIET', false);
$dbs = array(
'sql' => new \DB\SQL('mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', ''),
// 'sql-sqlite' => new \DB\SQL('sqlite:data/sqlite.db'),
// 'sql-pgsql' => new \DB\SQL('pgsql:host=localhost;dbname=fatfree', 'fatfree', 'fatfree'),
'jig' => new \DB\Jig('data/'),
'mongo' => new \DB\Mongo('mongodb://localhost:27017', 'testdb'),
// 'sqlsrv2012' => new \DB\SQL('sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'),
// 'sqlsrv2008' => new \DB\SQL('sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'),
);
$results = array();
// Test Syntax
foreach ($dbs as $type => $db) {
$test = new \Test_Syntax();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Test Relations
foreach ($dbs as $type => $db) {
$f3->set('DB',$db);
$test = new \Test_Relation();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Test Filter
foreach ($dbs as $type => $db) {
$f3->set('DB',$db);
$test = new \Test_Filter();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Further Common Tests
if (isset($dbs['sql'])) {
$test = new \Test_Common();
$f3->set('DB', $dbs['sql']);
$results = array_merge((array) $results, (array) $test->run());
}
$f3->set('results', $results);
}
}

View File

@@ -1,27 +0,0 @@
<?php
class AuthorModel extends \DB\Cortex {
protected
$fieldConf = array(
'name' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'mail' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'website' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'news' => array(
'has-many' => array('\NewsModel','author'),
),
'profile' => array(
'has-one' => array('\ProfileModel','author'),
),
),
// $primary = 'aid',
$table = 'author',
$db = 'DB';
}

View File

@@ -1,28 +0,0 @@
<?php
class NewsModel extends \DB\Cortex {
protected
$fieldConf = array(
'title' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR128
),
'text' => array(
'type' => \DB\SQL\Schema::DT_TEXT
),
'author' => array(
'belongs-to-one' => '\AuthorModel',
),
'tags' => array(
'belongs-to-many' => '\TagModel',
),
'tags2' => array(
'has-many' => array('\TagModel','news','news_tags'),
// 'has-many' => array('\TagModel','news'),
),
),
// $primary='nid',
$table = 'news',
$db = 'DB';
}

View File

@@ -1,21 +0,0 @@
<?php
class ProfileModel extends \DB\Cortex {
protected
$fieldConf = array(
'message' => array(
'type' => \DB\SQL\Schema::DT_TEXT
),
'image' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'author' => array(
'belongs-to-one' => '\AuthorModel'
)
),
// $primary = 'profile_id',
$table = 'profile',
$db = 'DB';
}

View File

@@ -1,18 +0,0 @@
<?php
class TagModel extends \DB\Cortex {
protected
$fieldConf = array(
'title' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR128
),
'news' => array(
'has-many' => array('\NewsModel','tags2','news_tags'),
),
),
// $primary = 'tid',
$table = 'tags',
$db = 'DB';
}

View File

@@ -1,125 +0,0 @@
<?php
use App\Controller;
class Test_Common {
function run()
{
$test = new \Test();
/** @var \Base $f3 */
$f3 = \Base::instance();
$news = new NewsModel();
$news->load();
$dummy = array(
'title'=>'copy test',
'text'=>'Lorem ipsum dolor sit amet.',
'author'=>1,
'tags'=>array(3)
);
$f3->set('record1', $dummy);
$news->copyto('record2');
$test->expect(
$f3->exists('record2'),
'copyto: raw record copied to hive'
);
$news->reset();
$news->copyfrom('record1');
$test->expect(
$news->title = 'copy test' &&
$news->text = 'Lorem ipsum dolor sit amet.',
'copyfrom: hydrate from hive key'
);
$test->expect(
$news->author instanceof AuthorModel
&& !$news->author->dry() &&
$news->tags instanceof \DB\CortexCollection,
'copyfrom: relations hydrated successful'
);
$test->expect(
$news->get('author',true) == 1,
'get raw data from relational field'
);
$news->reset();
$news->copyfrom('record2','title;author');
$test->expect(
$news->title = 'Responsive Images' &&
$news->get('author',true) == 2 &&
$news->text == NULL,
'copyfrom: limit fields with split-able string'
);
$news->reset();
$news->copyfrom('record2',array('title'));
$test->expect(
$news->title = 'Responsive Images' && $news->text == NULL,
'copyfrom: limit fields by array'
);
$news->reset();
$news->copyfrom($dummy,function($fields) {
return array_intersect_key($fields,array_flip(array('title')));
});
$test->expect(
$news->title = 'copy test',
'copyfrom: copy from array instead of hive key'
);
$test->expect(
$news->title = 'copy test' && $news->text == NULL,
'copyfrom: limit fields by callback function'
);
$all = $news->find();
$allTitle = $all->getAll('title');
$test->expect(
count($allTitle) == 3 &&
$allTitle[0] == 'Responsive Images' &&
$allTitle[1] == 'CSS3 Showcase' &&
$allTitle[2] == 'Touchable Interfaces',
'collection getAll returns all values of selected field'
);
$newsByID = $all->getBy('_id');
$test->expect(
array_keys($newsByID) == array(1,2,3),
'collection getBy sorts by given field'
);
$newsByAuthorID = $all->getBy('author',true);
$test->expect(
array_keys($newsByAuthorID) == array(2, 1) &&
count($newsByAuthorID[2]) == 2 &&
count($newsByAuthorID[1]) == 1,
'collection getBy nested sort by author'
);
$allTitle = array();
foreach($all as $record)
$allTitle[] = $record->title;
$test->expect(
count($allTitle) == 3 &&
$allTitle[0] == 'Responsive Images' &&
$allTitle[1] == 'CSS3 Showcase' &&
$allTitle[2] == 'Touchable Interfaces',
'collection is traversable'
);
///////////////////////////////////
return $test->results();
}
}

View File

@@ -1,302 +0,0 @@
<?php
use App\Controller;
class Test_Filter {
function run($db,$type)
{
$test = new \Test();
// setup
///////////////////////////////////
$author = new \AuthorModel();
$news = new \NewsModel();
$profile = new \ProfileModel();
$tag = new \TagModel();
$ac=$author::resolveConfiguration();
$author_pk = (is_int(strpos($type,'sql'))?$ac['primary']:'_id');
$nc=$news::resolveConfiguration();
$news_pk = (is_int(strpos($type,'sql'))?$nc['primary']:'_id');
$tc=$tag::resolveConfiguration();
$tag_pk = (is_int(strpos($type,'sql'))?$tc['primary']:'_id');
$authorIDs = $author->find()->getAll('_id');
$all = $news->find();
$newsIDs = $all->getAll('_id');
$profileIDs = $profile->find()->getAll('_id');
$tagIDs = $tag->find()->getAll('_id');
// add another relation
$news->load(array('title = ?','CSS3 Showcase'));
$news->author = $author->load(array($author_pk.' = ?',$authorIDs[0]));
$news->save();
$news->reset();
$author->reset();
// has-filter on belongs-to relation
///////////////////////////////////
$result = $author->has('news', array('title like ?', '%Image%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'Johnny English',
$type.': has filter on many-to-one field'
);
$test->expect(
count($result[0]['news']) == 2 &&
$result[0]['news'][0]['title'] == 'Responsive Images' &&
$result[0]['news'][1]['title'] == 'CSS3 Showcase',
$type.': has filter does not prune relation set'
);
$result = $news->has('author', array('name = ?', 'Johnny English'))->afind();
$test->expect(
count($result) == 2 && // has 2 news
$result[0]['title'] == 'Responsive Images' &&
$result[1]['title'] == 'CSS3 Showcase',
$type.': has filter on one-to-many field'
);
// add another profile
$profile->message = 'Beam me up, Scotty!';
$profile->author = $authorIDs[2];
$profile->save();
$profile->reset();
$result = $author->has('profile',array('message LIKE ?','%Scotty%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'James T. Kirk' &&
$result[0]['profile']['message'] == 'Beam me up, Scotty!',
$type.': has filter on one-to-one field'
);
$result = $profile->has('author',array('name LIKE ?','%Kirk%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['message'] == 'Beam me up, Scotty!' &&
$result[0]['author']['name'] == 'James T. Kirk',
$type.': has filter on one-to-one field, inverse'
);
// add mm tags
$news->load(array('title = ?','Responsive Images'));
$news->tags2 = array($tagIDs[0],$tagIDs[1]);
$news->save();
$news->load(array('title = ?','CSS3 Showcase'));
$news->tags2 = array($tagIDs[1],$tagIDs[2]);
$news->save();
$news->reset();
$result = $news->has('tags2',array('title like ?','%Design%'))->find();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': has filter on many-to-many field'
);
$result = $news->has('tags2',array('title = ?','Responsive'))->find();
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Responsive Images' &&
$result[1]['title'] == 'CSS3 Showcase',
$type.': has filter on many-to-many field, additional test'
);
$result = $tag->has('news',array('title = ?','Responsive Images'))->find();
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Web Design' &&
$result[1]['title'] == 'Responsive',
$type.': has filter on many-to-many field, inverse'
);
// add another tag
$news->load(array('title = ?', 'Touchable Interfaces'));
$news->tags2 = array($tagIDs[1]);
$news->save();
$news->reset();
$tag->has('news',array('text LIKE ? and title LIKE ?', '%Lorem%', '%Interface%'));
$result = $tag->find();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive',
$type.': has filter with multiple conditions'
);
$news->has('tags2', array('title = ? OR title = ?', 'Usability', 'Web Design'));
$result = $news->afind(array('text = ?', 'Lorem Ipsun'));
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': find with condition and has filter'
);
$news->load(array('title = ?', 'Responsive Images'));
$news->author = $authorIDs[1];
$news->save();
$news->reset();
$news->has('tags2', array('title = ? OR title = ?', 'Usability', 'Web Design'));
$news->has('author', array('name = ?', 'Ridley Scott'));
$result = $news->afind();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': find with multiple has filters on different relations'
);
// add another news to author 2
$news->load(array($news_pk.' = ?',$newsIDs[2]));
$news->author = $authorIDs[1];
$news->save();
$news->reset();
$news->has('author', array('name = ?', 'Ridley Scott'));
$news->load();
$res = array();
while (!$news->dry()) {
$res[] = $news->title;
$news->next();
}
$test->expect(
count($res) == 2 &&
$res[0] == 'Responsive Images' &&
$res[1] == 'Touchable Interfaces'
,
$type.': has filter in load context'
);
$news->reset();
$news->fields(array('title'));
$news->load();
$test->expect(
!empty($news->title) &&
empty($news->author) &&
empty($news->text) &&
empty($news->tags) &&
empty($news->tags2),
$type.': use a whitelist to restrict fields'
);
unset($news);
$news = new \NewsModel();
$news->fields(array('title','tags','tags2','author'),true);
$news->load();
$test->expect(
empty($news->title) &&
empty($news->author) &&
!empty($news->text) &&
empty($news->tags) &&
empty($news->tags2),
$type.': use a blacklist to restrict fields'
);
unset($news);
$news = new \NewsModel();
$news->fields(array('tags.title'));
$news->load();
$test->expect(
!empty($news->tags[0]->title) &&
empty($news->tags[0]->news),
$type.': set restricted fields to related mappers'
);
$news->filter('tags2',null,array('order'=>'title ASC'));
$news->load(array('title = ?','Responsive Images'));
$test->expect(
$news->tags2[0]->title == 'Responsive' &&
$news->tags2[1]->title == 'Web Design',
$type.': filter with sorting of related records'
);
// get all tags sorted by their usage in news articles
$tag->reset();
$tag->countRel('news');
$result = $tag->find(null,array('order'=>'count_news DESC, title'))->castAll(0);
$test->expect(
$result[0]['title'] == 'Responsive' &&
$result[0]['count_news'] == 3 &&
$result[1]['title'] == 'Usability' &&
$result[1]['count_news'] == 1 &&
$result[2]['title'] == 'Web Design' &&
$result[2]['count_news'] == 1,
$type.': count and sort on many-to-many relation'
);
// get all authors sorted by the amount of news they have written
$author->reset();
$author->countRel('news');
$result = $author->find(null,array('order'=>'count_news DESC'))->castAll(0);
$test->expect(
$result[0]['name'] == 'Ridley Scott' &&
$result[0]['count_news'] == 2 &&
$result[1]['name'] == 'Johnny English' &&
$result[1]['count_news'] == 1 &&
$result[2]['name'] == 'James T. Kirk' &&
$result[2]['count_news'] == null,
$type.': count and sort on one-to-many relation'
);
$tag->reset();
$tag->countRel('news');
$result = $tag->find(null,array('order'=>'count_news DESC, title DESC','limit'=>1,'offset'=>1))->castAll(0);
$test->expect(
$result[0]['title'] == 'Web Design' &&
$result[0]['count_news'] == 1,
$type.': apply limit and offset on aggregated collection'
);
$author->reset();
$author->countRel('news');
$author->has('news',array('text like ?','%Lorem%'));
$result = $author->find()->castAll(0);
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'Ridley Scott' &&
$result[0]['count_news'] == 2 ,
$type.': has-filter and 1:M relation counter'
);
$author->reset();
$id = $author->load()->next()->_id;
$tag->reset();
$tag->countRel('news');
$tag->has('news',array('author = ?',$id));
$result = $tag->find(null,array('order'=>'count_news desc'))->castAll(0);
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Responsive' &&
$result[0]['count_news'] == 3 &&
$result[1]['title'] == 'Web Design' &&
$result[1]['count_news'] == 1,
$type.': has-filter and M:M relation counter'
);
///////////////////////////////////
return $test->results();
}
}

View File

@@ -1,335 +0,0 @@
<?php
use App\Controller;
class Test_Relation {
/**
* unify results for better comparison
*/
private function getResult($result)
{
$out = array();
foreach ($result as $row) {
if(is_object($row))
$row = $row->cast();
unset($row['_id']);
unset($row['id']);
unset($row['aid']);
unset($row['uid']);
unset($row['nid']);
unset($row['tid']);
unset($row['pid']);
unset($row['profile_id']);
foreach ($row as $col => $val) {
if (empty($val) || is_null($val))
unset($row[$col]);
}
$out[] = $row;
}
return $out;
}
function run($db,$type)
{
$test = new \Test();
// clear existing data
\AuthorModel::setdown();
\TagModel::setdown();
\NewsModel::setdown();
\ProfileModel::setdown();
// setup models
\AuthorModel::setup();
\TagModel::setup();
\NewsModel::setup();
\ProfileModel::setup();
// setup Author
///////////////////////////////////
$author_id = array();
$author = new \AuthorModel();
$ac=$author::resolveConfiguration();
$author_pk = (is_int(strpos($type,'sql'))?$ac['primary']:'_id');
$author->name = 'Johnny English';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$author->name = 'Ridley Scott';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$author->name = 'James T. Kirk';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$allauthors = $author->find()->castAll();
$allauthors = $this->getResult($allauthors);
$test->expect(
json_encode($allauthors) ==
'[{"name":"Johnny English"},{"name":"Ridley Scott"},{"name":"James T. Kirk"}]',
$type.': all AuthorModel items created'
);
// setup Tags
///////////////////////////////////
$tag_id = array();
$tag = new \TagModel();
$tc=$tag::resolveConfiguration();
$tag_pk = (is_int(strpos($type,'sql'))?$tc['primary']:'_id');
$tag->title = 'Web Design';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$tag->title = 'Responsive';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$tag->title = 'Usability';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$allTags = $this->getResult($tag->find());
$test->expect(
json_encode($allTags) ==
'[{"title":"Web Design"},{"title":"Responsive"},{"title":"Usability"}]',
$type.': all TagModel items created'
);
// setup News
///////////////////////////////////
$news_id = array();
$news = new \NewsModel();
$nc=$news::resolveConfiguration();
$news_pk = (is_int(strpos($type,'sql'))?$nc['primary']:'_id');
$news->title = 'Responsive Images';
$news->text = 'Lorem Ipsun';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$news->title = 'CSS3 Showcase';
$news->text = 'News Text 2';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$news->title = 'Touchable Interfaces';
$news->text = 'Lorem Foo';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$allnews = $this->getResult($news->find());
$test->expect(
json_encode($allnews) ==
'[{"title":"Responsive Images","text":"Lorem Ipsun"},{"title":"CSS3 Showcase","text":"News Text 2"},{"title":"Touchable Interfaces","text":"Lorem Foo"}]',
$type.': all NewsModel items created'
);
// belongs-to author relation
///////////////////////////////////
$author->load();
$news->load(array($news_pk.' = ?',$news_id[0]));
$news->author = $author;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->author->name == 'Johnny English',
$type.': belongs-to-one: author relation created'
);
$news->author = NULL;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
empty($news->author),
$type.': belongs-to-one: author relation released'
);
$news->author = $author->_id;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->author->name == 'Johnny English',
$type.': belongs-to-one: relation created by raw id'
);
// belongs-to-many tag relation
///////////////////////////////////
$tag1 = new \TagModel();
$tag1->load(array($tag_pk.' = ?', $tag_id[0]));
$tag2 = new \TagModel();
$tag2->load(array($tag_pk.' = ?', $tag_id[1]));
$news->tags = array($tag1,$tag2);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->tags[0]->title == 'Web Design' && $news->tags[1]->title == 'Responsive',
$type.': belongs-to-many: relations created with array of mapper objects'
);
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$news->tags = array($tag_id[1],$tag_id[2]);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
$news->tags[0]->title == 'Responsive' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with array of IDs'
);
$news->tags = null;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
empty($news->tags),
$type.': belongs-to-many: relations released'
);
$tag->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$news->tags = $tag->load(array($tag_pk.' != ?',$tag_id[0]));
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
$news->tags[0]->title == 'Responsive' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with hydrated mapper'
);
$news->reset();
$tag->reset();
$news->load(array($news_pk.' = ?', $news_id[2]));
$news->tags = $tag_id[0].';'.$tag_id[2];
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[2]));
$test->expect(
$news->tags[0]->title == 'Web Design' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with split-able string'
);
$test->expect(
is_object($news->tags) && $news->tags instanceof \DB\CortexCollection,
$type.': belongs-to-many: result is collection'
);
// has-one relation
///////////////////////////////////
$profile = new ProfileModel();
$pc=$profile::resolveConfiguration();
$profile_pk = (is_int(strpos($type,'sql'))?$pc['primary']:'_id');
$profile->message = 'Hello World';
$profile->author = $author->load(array($author_pk.' = ?',$author_id[0]));
$profile->save();
$profile_id = $profile->_id;
$profile->reset();
$author->reset();
$author->load(array($author_pk.' = ?', $author_id[0]));
$profile->load(array($profile_pk.' = ?', $profile_id));
$test->expect(
$author->profile->message == 'Hello World' &&
$profile->author->name == "Johnny English",
$type.': has-one: relation assigned'
);
$profile->reset();
$profile->message = 'I\'m feeling lucky';
$profile->image = 'lolcat.jpg';
$author->reset();
$author->load(array($author_pk.' = ?',$author_id[1]));
$author->profile = $profile;
$author->save();
$profile->reset();
$author->reset();
$author->load(array($author_pk.' = ?', $author_id[1]));
$test->expect(
$author->profile->message == 'I\'m feeling lucky',
$type.': has-one: inverse relation'
);
// has-many relation
///////////////////////////////////
$author->load(array($author_pk.' = ?', $author_id[0]));
$result = $this->getResult($author->news);
$test->expect(
$result[0]['title'] == "Responsive Images" &&
$result[0]['tags'][0]['title'] == 'Web Design' &&
$result[0]['tags'][1]['title'] == 'Responsive',
$type.': has-many inverse relation'
);
// many to many relation
///////////////////////////////////
$news->load(array($news_pk.' = ?',$news_id[0]));
$news->tags2 = array($tag_id[0],$tag_id[1]);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?',$news_id[0]));
$test->expect(
$news->tags2[0]['title'] == 'Web Design' &&
$news->tags2[1]['title'] == 'Responsive',
$type.': many-to-many relation created'
);
$test->expect(
is_object($news->tags2) && $news->tags2 instanceof \DB\CortexCollection,
$type.': many-to-many: result is collection'
);
$news->load(array($news_pk.' = ?', $news_id[0]));
$news->tags2 = NULL;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
is_null($news->tags2),
$type.': many-to-many relation released'
);
$all = $news->find();
$test->expect(
$all[1]->tags2 === NULL
&& $all[2]->author === NULL,
$type.': empty relations are NULL'
);
$arr = $news->cast();
$test->expect(
is_array($arr['tags']),
$type.': collection becomes array in casted model'
);
if ($type == 'mongo') {
$test->expect(
is_string($arr['_id']),
$type.': id becomes string in casted model'
);
}
///////////////////////////////////
return $test->results();
}
}

View File

@@ -1,364 +0,0 @@
<?php
use App\Controller;
class Test_Syntax {
function run($db,$type)
{
$test = new \Test();
$tname = 'test_cortex';
\DB\Cortex::setdown($db, $tname);
$fields = array(
'title' => array('type' => \DB\SQL\Schema::DT_TEXT),
'num1' => array('type' => \DB\SQL\Schema::DT_INT4),
'num2' => array('type' => \DB\SQL\Schema::DT_INT4),
);
\DB\Cortex::setup($db, $tname, $fields);
// adding some testing data
$cx = new \DB\Cortex($db, $tname);
$cx->title = 'bar1';
$cx->save();
$cx->reset();
$cx->title = 'baz2';
$cx->num1 = 1;
$cx->save();
$cx->reset();
$cx->title = 'foo3';
$cx->num1 = 4;
$cx->save();
$cx->reset();
$cx->title = 'foo4';
$cx->num1 = 3;
$cx->save();
$cx->reset();
$cx->title = 'foo5';
$cx->num1 = 3;
$cx->num2 = 5;
$cx->save();
$cx->reset();
$cx->title = 'foo6';
$cx->num1 = 3;
$cx->num2 = 1;
$cx->save();
$cx->reset();
$cx->title = 'foo7';
$cx->num1 = 3;
$cx->num2 = 10;
$cx->save();
$cx->reset();
$cx->title = 'foo8';
$cx->num1 = 5;
$cx->save();
$cx->reset();
$cx->title = 'foo9';
$cx->num1 = 8;
$cx->save();
$cx->reset();
$result = $this->getResult($cx->find());
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
2 => array(
'num1' => 4,
'title' => 'foo3',
),
3 => array(
'num1' => 3,
'title' => 'foo4',
),
4 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
5 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
6 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
7 => array(
'num1' => 5,
'title' => 'foo8',
),
8 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': init mapper, adding records'
);
// operator =
$result = $this->getResult($cx->find(array('title = ?', 'foo7')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: ='
);
// operator >
$result = $this->getResult($cx->find(array('num1 > ?', 4)));
$expected = array(
0 => array(
'num1' => 5,
'title' => 'foo8',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: >'
);
// operator >=
$result = $this->getResult($cx->find(array('num1 >= ?', 5)));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: >='
);
// operator <
$result = $this->getResult($cx->find(array('num2 < ?', 2)));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: <'
);
// operator <=
$result = $this->getResult($cx->find(array('num2 <= ?', 1)));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: <='
);
// operator without binding
$result = $this->getResult($cx->find(array('num1 > 4')));
$expected = array(
0 => array(
'num1' => 5,
'title' => 'foo8',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator without binding'
);
// field comparision
$result = $this->getResult($cx->find(
array('num2 > num1', 1)));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
1 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check field comparision'
);
// lookahead search
$result = $this->getResult($cx->find(array('title like ?', '%o6')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': lookahead search'
);
// lookbehind search
$result = $this->getResult($cx->find(array('title like ?', 'bar%')));
$expected = array(
0 => array(
'title' => 'bar1',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': lookbehind search'
);
// full search
$result = $this->getResult($cx->find(array('title like ?', '%a%')));
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': full search'
);
// negated search
$result = $this->getResult($cx->find(array('title not like ?', 'foo%')));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': negated search'
);
// AND / OR chaining
$result = $this->getResult($cx->find(
array('(num2 < ? AND num1 > ?) OR title like ?', 2, 1, '%o9')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check logical operator chaining'
);
// check limit
$result = $this->getResult($cx->find(
null, array('limit' => '2')));
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check limit'
);
// check order
$result = $this->getResult($cx->find(
array('num2 >= ?', 1), array('order' => 'num2 desc')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
1 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
2 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check order'
);
// IN search
$rc = $cx->find(array('num1 IN ?',array(4,5,8)));
$result = $rc->getAll('title');
sort($result);
$test->expect(
json_encode($result) == json_encode(array('foo3','foo8','foo9')),
$type.': IN operator'
);
$rc = $cx->find(array('num1 IN ? && num2 > ? && num2 NOT IN ?',array(3,4),1,array(10)));
$result = $rc->getAll('title');
$test->expect(
json_encode($result) == json_encode(array('foo5')),
$type.': enhanced IN, NOT IN operator'
);
///////////////////////////////////
return $test->results();
}
/**
* unify results for better comparison
*/
private function getResult($result)
{
$out = array();
foreach ($result as $row) {
$row = $row->cast();
unset($row['_id']);
unset($row['id']);
ksort($row);
foreach ($row as $col => $val) {
if (empty($val) || is_null($val))
unset($row[$col]);
}
$out[] = $row;
}
return $out;
}
}

View File

@@ -1,668 +0,0 @@
<?php
namespace App;
class Schema extends Controller
{
private
$roundTime = 0,
$current_test = 1,
$current_engine,
$f3,
$test,
$tname;
private function getTime()
{
$time = microtime(TRUE) - $this->f3->get('timer') - $this->roundTime;
$this->roundTime = microtime(TRUE) - $this->f3->get('timer');
return ' [ '.sprintf('%.3f', $time).'s ]';
}
private function getTestDesc($text)
{
return $this->getTime().' '.$this->current_engine.': #'.$this->current_test++.' - '.$text;
}
function get()
{
$this->f3 = \Base::instance();
$this->test = new \Test;
$this->f3->set('QUIET', false);
$this->f3->set('CACHE', false);
$dbs = array(
'mysql' => new \DB\SQL(
'mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', ''
),
'sqlite' => new \DB\SQL(
'sqlite::memory:'
// 'sqlite:db/sqlite.db'
),
'pgsql' => new \DB\SQL(
'pgsql:host=localhost;dbname=fatfree', 'fatfree', 'fatfree'
),
// 'sqlsrv2012' => new \DB\SQL(
// 'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'
// ),
// 'sqlsrv2008' => new \DB\SQL(
// 'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'
// )
);
$this->roundTime = microtime(TRUE) - \Base::instance()->get('timer');
$this->tname = 'test_table';
foreach ($dbs as $type => $db) {
$this->current_engine = $type;
$this->runTestSuite($db);
$this->current_test = 1;
}
$this->f3->set('results', $this->test->results());
}
private function runTestSuite($db)
{
$schema = new \DB\SQL\Schema($db);
$schema->dropTable($this->tname);
// create table
$table = $schema->createTable($this->tname);
$table = $table->build();
$result = $schema->getTables();
$this->test->expect(
in_array($this->tname, $result),
$this->getTestDesc('create default table')
);
unset($result);
$this->test->expect(
$table instanceof \DB\SQL\TableModifier,
$this->getTestDesc('$table->build() returns TableModifier')
);
// drop table
$table->drop();
$this->test->expect(
in_array($this->tname, $schema->getTables()) == false,
$this->getTestDesc('drop table')
);
unset($table);
// create table with columns
$table = $schema->createTable($this->tname);
$table->addColumn('title')->type($schema::DT_VARCHAR128);
$table->addColumn('number')->type($schema::DT_INT4);
$table = $table->build();
$r1 = $schema->getTables();
$r2 = $table->getCols();
$this->test->expect(
in_array($this->tname, $r1) && in_array('id', $r2)
&& in_array('title', $r2) && in_array('number', $r2),
$this->getTestDesc('create new table with additional columns')
);
unset($r1,$r2);
// testing all datatypes
foreach (array_keys($schema->dataTypes) as $index => $field) {
// testing column type
$table->addColumn('column_'.$index)->type($field);
$table->build();
$r1 = $table->getCols();
$this->test->expect(
in_array('column_'.$index, $r1),
$this->getTestDesc('adding column ['.$field.'], nullable')
);
}
$r1 = $table->getCols(true);
foreach (array_keys($schema->dataTypes) as $index => $field) {
if (isset($r1['column_'.$index])) {
$datType=$schema->findQuery($schema->dataTypes[$field]);
$compatible = $schema->isCompatible($field,$r1['column_'.$index]['type']);
$this->test->expect(
$compatible,
$this->getTestDesc('reverse lookup compatible: '.
($compatible?'YES':'NO').
', '.$field.': ['.$datType.' > '.$r1['column_'.$index]['type'].']')
);
}
}
unset($r1);
// adding some testing data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_5 = 123.456;
$mapper->column_6 = 123456.789012;
$mapper->column_7 = 'hello world';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('column_7 = ?', 'hello world'))->cast();
unset($mapper);
$this->test->expect(
$result['column_7'] == 'hello world',
$this->getTestDesc('mapping dummy data')
);
$this->test->expect(
$result['column_5'] == 123.456,
$this->getTestDesc('testing float value: '.$result['column_5'])
);
$this->test->expect(
$result['column_6'] == 123456.789012,
$this->getTestDesc('testing decimal value: '.$result['column_6'])
);
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->load();
$num = $this->current_engine == 'sqlite' ? '123456789.012345' : '123456789012.345678';
$mapper->column_6 = $num;
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('column_7 = ?', 'hello world'))->cast();
$this->test->expect(
$result['column_6'] == $num,
$this->getTestDesc('testing max decimal precision: '.$result['column_6'])
);
unset($mapper);
// default value text, not nullable
$table->addColumn('text_default_not_null')
->type($schema::DT_VARCHAR128)
->nullable(false)->defaults('foo bar');
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('text_default_not_null', array_keys($r1)) &&
$r1['text_default_not_null']['default'] == 'foo bar' &&
$r1['text_default_not_null']['nullable'] == false,
$this->getTestDesc('adding column [VARCHAR128], not nullable with default value')
);
unset($r1);
// some testing dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'tanduay';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('column_7 = ?','tanduay'))->cast();
$this->test->expect(
$result['column_7'] == 'tanduay' &&
$result['text_default_not_null'] == 'foo bar',
$this->getTestDesc('mapping dummy data')
);
unset($mapper,$result);
// default value numeric, not nullable
$table->addColumn('int_default_not_null')
->type($schema::DT_INT4)->nullable(false)->defaults(123);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('int_default_not_null', array_keys($r1)) &&
$r1['int_default_not_null']['default'] == 123 &&
$r1['int_default_not_null']['nullable'] == false,
$this->getTestDesc('adding column [INT4], not nullable with default value')
);
unset($r1);
// adding testing data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test3';
$mapper->save();
$mapper->reset();
$r1 = $mapper->findone(array('column_7 = ?','test3'))->cast();
$this->test->expect(
$r1['column_7'] == 'test3' &&
$r1['int_default_not_null'] == 123,
$this->getTestDesc('mapping dummy data')
);
unset($mapper,$r1);
// default value text, nullable
$table->addColumn('text_default_nullable')
->type($schema::DT_VARCHAR128)
->defaults('foo bar');
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('text_default_nullable', array_keys($r1)) &&
$r1['text_default_nullable']['default'] == 'foo bar',
$this->getTestDesc('adding column [VARCHAR128], nullable with default value')
);
unset($r1);
// adding some dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test4';
$mapper->save();
$mapper->reset();
$mapper->column_7 = 'test5';
$mapper->text_default_nullable = null;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ? OR column_7 = ?','test4','test5'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && array_key_exists(1, $result) &&
$result[0]['column_7'] == 'test4' && $result[0]['text_default_nullable'] == 'foo bar' &&
$result[1]['column_7'] == 'test5' && $result[1]['text_default_nullable'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// default value numeric, nullable
$table->addColumn('int_default_nullable')->type($schema::DT_INT4)->defaults(123);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('int_default_nullable', array_keys($r1)) == true &&
$r1['int_default_nullable']['default'] == 123,
$this->getTestDesc('adding column [INT4], nullable with default value')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test6';
$mapper->save();
$mapper->reset();
$mapper->column_7 = 'test7';
$mapper->int_default_nullable = null;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ? OR column_7 = ?', 'test6', 'test7'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && array_key_exists(1, $result) &&
$result[0]['column_7'] == 'test6' && $result[0]['int_default_nullable'] === 123 &&
$result[1]['column_7'] == 'test7' && $result[1]['int_default_nullable'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// current timestamp
$table->addColumn('stamp')
->type($schema::DT_TIMESTAMP)
->nullable(false)
->defaults($schema::DF_CURRENT_TIMESTAMP);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('stamp', array_keys($r1)) &&
$r1['stamp']['default'] == $schema::DF_CURRENT_TIMESTAMP,
$this->getTestDesc(
'adding column [TIMESTAMP], not nullable with current_timestamp default value')
);
unset($r1);
// datetime nullable
$table->addColumn('datetime')->type_datetime()->nullable(true);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('datetime', array_keys($r1)) &&
$r1['datetime']['nullable'] == true,
$this->getTestDesc(
'adding column [DATETIME], nullable, no default')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test_datetime';
$mapper->datetime = NULL;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ?', 'test_datetime'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && $result[0]['column_7'] == 'test_datetime' &&
$result[0]['datetime'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// rename column
$table->renameColumn('text_default_not_null', 'title123');
$table->build();
$r1 = $table->getCols();
$this->test->expect(
in_array('title123', $r1) && !in_array('text_default_not_null', $r1),
$this->getTestDesc('renaming column')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->title123 = 'test8';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('title123 = ?','test8'));
$this->test->expect(
!$result->dry(),
$this->getTestDesc('mapping dummy data')
);
$table->renameColumn('title123', 'text_default_not_null');
$table->build();
unset($result,$mapper);
// remove column
$table->dropColumn('column_1');
$table->build();
$r1 = $table->getCols();
$this->test->expect(
!in_array('column_1', $r1),
$this->getTestDesc('removing column')
);
unset($r1);
// rename table
$schema->dropTable('test123');
$table->rename('test123');
$result = $schema->getTables();
$this->test->expect(
in_array('test123', $result) && !in_array($this->tname, $result),
$this->getTestDesc('renaming table')
);
$table->rename($this->tname);
unset($result);
// check record count
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$this->test->expect(
count($mapper->find()) == 9,
$this->getTestDesc('check record count')
);
unset($mapper);
// adding composite primary keys
$table->addColumn('version')->type($schema::DT_INT4)->nullable(false)->defaults(1);
$table->primary(array('id', 'version'));
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(!empty($r1) && isset($r1['version']) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('adding composite primary-keys')
);
unset($r1);
// check record count
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$this->test->expect(
count($mapper->find()) == 9,
$this->getTestDesc('check record count')
);
unset($mapper);
// drop table
$schema->dropTable($this->tname);
$this->test->expect(
!in_array($this->tname, $schema->getTables()),
$this->getTestDesc('drop table')
);
// adding composite primary keys
$table = $schema->createTable($this->tname);
$table->addColumn('version')->type($schema::DT_INT4)
->defaults(1)->nullable(false);
$table->primary(array('id', 'version'));
$table = $table->build();
$r1 = $table->getCols(true);
$this->test->expect(!empty($r1) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('creating new table with composite key')
);
$this->test->expect(!empty($r1) &&
$r1['version']['default'] == '1',
$this->getTestDesc('default value on composite primary key')
);
unset($r1);
// more fields to composite primary key table
$table->addColumn('title')->type($schema::DT_VARCHAR256);
$table->addColumn('title2')->type($schema::DT_TEXT);
$table->addColumn('title_notnull')
->type($schema::DT_VARCHAR128)->nullable(false)->defaults("foo");
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('title', $r1) &&
array_key_exists('title_notnull', $r1) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('adding more fields to composite pk table')
);
unset($r1);
// testing primary keys with inserted data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->title = 'test1';
$mapper->save();
$mapper->reset();
$mapper->id = 1;
$mapper->title = 'nullable';
$mapper->version = 2;
$mapper->save();
$mapper->reset();
$mapper->title = 'test3';
$mapper->title2 = 'foobar';
$mapper->title_notnull = 'bar';
$mapper->save();
$result = array_map(array($mapper,'cast'),$mapper->find());
$cpk_expected = array(
0=>array(
'id' => 1,
'version' => 1,
'title' => 'test1',
'title2' => NULL,
'title_notnull' => 'foo',
),
1=>array(
'id' => 1,
'version' => 2,
'title' => 'nullable',
'title2' => NULL,
'title_notnull' => 'foo',
),
2=>array(
'id' => 2,
'version' => 1,
'title' => 'test3',
'title2' => 'foobar',
'title_notnull' => 'bar',
),
);
foreach ($result as &$r)
ksort($r);
foreach ($cpk_expected as &$r)
ksort($r);
$this->test->expect(
json_encode($result) == json_encode($cpk_expected),
$this->getTestDesc('adding items with composite primary-keys')
);
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->load();
$rec_count_cur = $mapper->loaded();
$schema->truncateTable($this->tname);
$mapper->reset();
$mapper->load();
$rec_count_new = $mapper->loaded();
$this->test->expect(
$rec_count_cur==3 && $rec_count_new == 0,
$this->getTestDesc('truncate table')
);
$schema->dropTable($this->tname);
// indexes
$table = $schema->createTable($this->tname);
$table->addColumn('rawtest', array('type' => $schema::DT_VARCHAR256, 'default' => 'foo'));
$table->addColumn('text')->type($schema::DT_TEXT);
$table->addColumn('foo')->type($schema::DT_VARCHAR128)->index();
$table = $table->build();
$r1 = $table->getCols(true);
$this->test->expect(
isset($r1['rawtest']) && $r1['rawtest']['default'] = 'foo',
$this->getTestDesc('adding column with options array')
);
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___foo']),
$this->getTestDesc('column index on table creation')
);
$table->addColumn('bar')->type($schema::DT_VARCHAR128)->index(true);
$table->addColumn('baz')->type($schema::DT_VARCHAR128);
$table->addIndex(array('foo', 'baz'));
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___bar']),
$this->getTestDesc('column index on table alteration')
);
$this->test->expect(
isset($indexes[$table->name.'___bar']) && $indexes[$table->name.'___bar']['unique'] == true,
$this->getTestDesc('unique index')
);
$this->test->expect(
isset($indexes[$table->name.'___foo__baz']),
$this->getTestDesc('index on combined columns')
);
if($this->current_engine == 'sqlite') {
$table->dropColumn('rawtest');
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___foo__baz']) && isset($indexes[$table->name.'___bar'])
&& $indexes[$table->name.'___bar']['unique'],
$this->getTestDesc('preserve indexes after table rebuild')
);
}
$table->dropIndex($table->name.'___bar');
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
!array_key_exists($table->name.'___bar',$indexes),
$this->getTestDesc('drop index')
);
// update column
$table->updateColumn('bar',$schema::DT_TEXT);
$table->build();
$r1 = $table->getCols(true);
$text = preg_match('/sybase|dblib|odbc|sqlsrv/',$this->current_engine)
? 'nvarchar' : 'text';
$this->test->expect(
array_key_exists('bar', $r1) && $r1['bar']['type'] == $text,
$this->getTestDesc('update column')
);
// update column
$cols = $table->getCols(true);
$bar = $cols['bar'];
$col = new \DB\SQL\Column('bar',$table);
$col->copyfrom($bar);
$col->type_varchar(60);
$col->defaults('great');
$table->updateColumn('bar',$col);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('bar', $r1)
&& $r1['bar']['default'] == 'great',
$this->getTestDesc('update column and default')
);
// update column default only
$cols = $table->getCols(true);
$bar = $cols['bar'];
$col = new \DB\SQL\Column('bar',$table);
$col->copyfrom($bar);
$col->passThrough();
$col->defaults('');
$table->updateColumn('bar',$col);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('bar', $r1) && $r1['bar']['default'] == '',
$this->getTestDesc('update default value')
);
$col->nullable(false);
$table->updateColumn('bar',$col);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('bar', $r1) && $r1['bar']['nullable'] == false,
$this->getTestDesc('update nullable flag')
);
// create table with text not nullable column
$table2 = $schema->createTable($this->tname.'_notnulltext');
$table2->addColumn('desc')->type($schema::DT_TEXT)->nullable(false);
$table2 = $table2->build();
$r1 = $schema->getTables();
$r2 = $table2->getCols(true);
$this->test->expect(
in_array($this->tname.'_notnulltext', $r1) && array_key_exists('desc', $r2)
&& $r2['desc']['nullable']==false,
$this->getTestDesc('create new table with not nullable text column')
);
$table2->drop();
// boolean fields are actually bit/tinyint
$schema->dropTable($this->tname.'_notnullbool');
$table2 = $schema->createTable($this->tname.'_notnullbool');
$table2->addColumn('active')->type($schema::DT_BOOL)->nullable(false);
$table2 = $table2->build();
$r1 = $schema->getTables();
$r2 = $table2->getCols(true);
$this->test->expect(
in_array($this->tname.'_notnullbool', $r1) && array_key_exists('active', $r2)
&& $r2['active']['nullable']==false,
$this->getTestDesc('create new table with not nullable boolean column')
);
$table2->addColumn('active2')->type($schema::DT_BOOL)->nullable(false)->defaults(0);
$table2->addColumn('active3')->type($schema::DT_BOOL)->nullable(false)->defaults(1);
$table2->build();
$r1 = $schema->getTables();
$r2 = $table2->getCols(true);
$this->test->expect(
in_array($this->tname.'_notnullbool', $r1)
&& array_key_exists('active2', $r2) && $r2['active2']['nullable']==false &&
((int)$r2['active2']['default']==0||$r2['active2']['default']=='false')
&& array_key_exists('active3', $r2) && $r2['active3']['nullable']==false &&
((int)$r2['active3']['default']==1||$r2['active3']['default']=='true'),
$this->getTestDesc('add not nullable boolean columns with default to existing table')
);
$table2->drop();
}
}

View File

@@ -19,6 +19,9 @@ class AccessController extends Controller {
* @param \Base $f3
* @param $params
* @return bool
* @throws \Exception
* @throws \Exception\PathfinderException
* @throws \ZMQSocketException
*/
function beforeroute(\Base $f3, $params): bool {
if($return = parent::beforeroute($f3, $params)){
@@ -47,6 +50,8 @@ class AccessController extends Controller {
* get current character and check if it is a valid character
* @param \Base $f3
* @return bool
* @throws \Exception
* @throws \Exception\PathfinderException
*/
protected function isLoggedIn(\Base $f3): bool {
$loginCheck = false;
@@ -66,6 +71,7 @@ class AccessController extends Controller {
* @param \Base $f3
* @param Model\CharacterModel $character
* @return bool
* @throws \Exception\PathfinderException
*/
private function checkLogTimer(\Base $f3, Model\CharacterModel $character){
$loginCheck = false;
@@ -97,6 +103,9 @@ class AccessController extends Controller {
* -> send over TCP Socket
* @param Model\MapModel $map
* @return int (number of active connections for this map)
* @throws \Exception
* @throws \Exception\PathfinderException
* @throws \ZMQSocketException
*/
protected function broadcastMapData(Model\MapModel $map){
$mapData = $this->getFormattedMapData($map);
@@ -107,6 +116,8 @@ class AccessController extends Controller {
* get formatted Map Data
* @param Model\MapModel $map
* @return array
* @throws \Exception
* @throws \Exception\PathfinderException
*/
protected function getFormattedMapData(Model\MapModel $map){
$mapData = $map->getData();

View File

@@ -34,6 +34,8 @@ class Admin extends Controller{
* @param \Base $f3
* @param $params
* @return bool
* @throws \Exception
* @throws \Exception\PathfinderException
*/
function beforeroute(\Base $f3, $params): bool {
$return = parent::beforeroute($f3, $params);
@@ -63,6 +65,7 @@ class Admin extends Controller{
/**
* event handler after routing
* @param \Base $f3
* @throws \Exception\PathfinderException
*/
public function afterroute(\Base $f3) {
// js view (file)
@@ -81,6 +84,7 @@ class Admin extends Controller{
* returns valid admin $characterModel for current user
* @param \Base $f3
* @return CharacterModel|null
* @throws \Exception
*/
protected function getAdminCharacter(\Base $f3){
$adminCharacter = null;
@@ -148,6 +152,7 @@ class Admin extends Controller{
* @param \Base $f3
* @param array $params
* @param null $character
* @throws \Exception\PathfinderException
*/
public function dispatch(\Base $f3, $params, $character = null){
if($character instanceof CharacterModel){
@@ -191,6 +196,7 @@ class Admin extends Controller{
* @param CharacterModel $character
* @param int $kickCharacterId
* @param int $minutes
* @throws \Exception\PathfinderException
*/
protected function kickCharacter(CharacterModel $character, $kickCharacterId, $minutes){
$kickOptions = self::KICK_OPTIONS;
@@ -220,6 +226,7 @@ class Admin extends Controller{
* @param CharacterModel $character
* @param int $banCharacterId
* @param int $value
* @throws \Exception\PathfinderException
*/
protected function banCharacter(CharacterModel $character, $banCharacterId, $value){
$banCharacters = $this->filterValidCharacters($character, $banCharacterId);
@@ -267,6 +274,7 @@ class Admin extends Controller{
* get log file for "admin" logs
* @param string $type
* @return \Log
* @throws \Exception\PathfinderException
*/
static function getLogger($type = 'ADMIN'){
return parent::getLogger('ADMIN');

View File

@@ -17,6 +17,7 @@ class Access extends Controller\AccessController {
* search character/corporation or alliance by name
* @param \Base $f3
* @param $params
* @throws \Exception
*/
public function search($f3, $params){

View File

@@ -17,6 +17,7 @@ class Connection extends Controller\AccessController {
* save a new connection or updates an existing (drag/drop) between two systems
* if a connection is changed (drag&drop) to another system. -> this function is called for update
* @param \Base $f3
* @throws \Exception
*/
public function save(\Base $f3){
$postData = (array)$f3->get('POST');

View File

@@ -21,6 +21,7 @@ class GitHub extends Controller\Controller {
/**
* get HTTP request options for API (curl) request
* @return array
* @throws \Exception\PathfinderException
*/
protected function getRequestOptions(){
$requestOptions = [
@@ -36,6 +37,7 @@ class GitHub extends Controller\Controller {
/**
* get release information from GitHub
* @param $f3
* @throws \Exception\PathfinderException
*/
public function releases($f3){
$cacheKey = 'CACHE_GITHUB_RELEASES';

View File

@@ -71,6 +71,8 @@ class Map extends Controller\AccessController {
/**
* Get all required static config data for program initialization
* @param \Base $f3
* @throws Exception
* @throws Exception\PathfinderException
*/
public function init(\Base $f3){
// expire time in seconds
@@ -159,6 +161,15 @@ class Map extends Controller\AccessController {
}
$return->connectionScopes = $connectionScopeData;
// get available wormhole types ---------------------------------------------------------------------------
$wormholes = Model\BasicModel::getNew('WormholeModel');
$rows = $wormholes->find('id > 0', null, $expireTimeSQL);
$wormholesData = [];
foreach((array)$rows as $rowData){
$wormholesData[$rowData->name] = $rowData->getData();
}
$return->wormholes = $wormholesData;
// get available character status -------------------------------------------------------------------------
$characterStatus = Model\BasicModel::getNew('CharacterStatusModel');
$rows = $characterStatus->find('active = 1', null, $expireTimeSQL);
@@ -223,6 +234,7 @@ class Map extends Controller\AccessController {
/**
* import new map data
* @param \Base $f3
* @throws Exception
*/
public function import(\Base $f3){
$importData = (array)$f3->get('POST');
@@ -367,6 +379,7 @@ class Map extends Controller\AccessController {
/**
* save a new map or update an existing map
* @param \Base $f3
* @throws Exception
*/
public function save(\Base $f3){
$formData = (array)$f3->get('POST.formData');
@@ -563,6 +576,7 @@ class Map extends Controller\AccessController {
/**
* delete a map and all dependencies
* @param \Base $f3
* @throws Exception
*/
public function delete(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
@@ -597,6 +611,9 @@ class Map extends Controller\AccessController {
* -> if characters with map access found -> broadcast mapData to them
* @param Model\MapModel $map
* @param array $characterIds
* @throws Exception
* @throws Exception\PathfinderException
* @throws \ZMQSocketException
*/
protected function broadcastMapAccess($map, $characterIds){
$mapAccess = [
@@ -614,6 +631,7 @@ class Map extends Controller\AccessController {
* broadcast map delete information to clients
* @param int $mapId
* @return bool|string
* @throws \ZMQSocketException
*/
protected function broadcastMapDeleted($mapId){
return (new Socket( Config::getSocketUri() ))->sendData('mapDeleted', $mapId);
@@ -623,6 +641,7 @@ class Map extends Controller\AccessController {
* get map access tokens for current character
* -> send access tokens via TCP Socket for WebSocket auth
* @param \Base $f3
* @throws Exception
*/
public function getAccessData(\Base $f3){
$return = (object) [];
@@ -656,6 +675,8 @@ class Map extends Controller\AccessController {
* update map data
* -> function is called continuously (trigger) by any active client
* @param \Base $f3
* @throws Exception
* @throws Exception\PathfinderException
*/
public function updateData(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
@@ -805,6 +826,8 @@ class Map extends Controller\AccessController {
* get formatted map data
* @param Model\MapModel[] $mapModels
* @return array
* @throws Exception
* @throws Exception\PathfinderException
*/
protected function getFormattedMapsData($mapModels){
$mapData = [];
@@ -819,6 +842,8 @@ class Map extends Controller\AccessController {
* update map data api
* -> function is called continuously by any active client
* @param \Base $f3
* @throws Exception
* @throws Exception\PathfinderException
*/
public function updateUserData(\Base $f3){
$return = (object) [];
@@ -896,6 +921,7 @@ class Map extends Controller\AccessController {
* @param Model\CharacterModel $character
* @param Model\MapModel $map
* @return Model\MapModel
* @throws Exception
*/
protected function updateMapData(Model\CharacterModel $character, Model\MapModel $map){
@@ -1056,21 +1082,34 @@ class Map extends Controller\AccessController {
}
}
// save connection ------------------------------------------------------------------------------------
if(
$addConnection &&
$sourceExists &&
$targetExists &&
$sourceSystem &&
$targetSystem &&
!$map->searchConnection( $sourceSystem, $targetSystem )
$targetSystem
){
$connection = $map->getNewConnection($sourceSystem, $targetSystem);
$connection = $map->saveConnection($connection, $character);
// get updated maps object
if($connection){
$map = $connection->mapId;
$mapDataChanged = true;
$connection = $map->searchConnection( $sourceSystem, $targetSystem);
// save connection --------------------------------------------------------------------------------
if(
$addConnection &&
!$connection
){
$connection = $map->getNewConnection($sourceSystem, $targetSystem);
$connection = $map->saveConnection($connection, $character);
// get updated maps object
if($connection){
$map = $connection->mapId;
$mapDataChanged = true;
}
}
// log jump mass ----------------------------------------------------------------------------------
if(
$connection &&
$connection->isWormhole()
){
$connection->logMass($log);
}
}
}
@@ -1087,9 +1126,13 @@ class Map extends Controller\AccessController {
/**
* get connectionData
* @param \Base $f3
* @throws Exception
*/
public function getConnectionData (\Base $f3){
$postData = (array)$f3->get('POST');
$addData = (array)$postData['addData'];
$filterData = (array)$postData['filterData'];
$connectionData = [];
if($mapId = (int)$postData['mapId']){
@@ -1102,13 +1145,27 @@ class Map extends Controller\AccessController {
$map->getById($mapId);
if($map->hasAccess($activeCharacter)){
$connections = $map->getConnections('wh');
foreach($connections as $connection){
$data = $connection->getData(true);
// skip connections whiteout signature data
if($data->signatures){
$connectionData[] = $data;
// get specific connections by id
$connectionIds = null;
if(is_array($postData['connectionIds'])){
$connectionIds = $postData['connectionIds'];
}
$connections = $map->getConnections($connectionIds, 'wh');
foreach($connections as $connection){
$check = true;
$data = $connection->getData(in_array('signatures', $addData), in_array('logs', $addData));
// filter result
if(in_array('signatures', $filterData) && !$data->signatures){
$check = false;
}
if(in_array('logs', $filterData) && !$data->logs){
$check = false;
}
if($check){
$connectionData[] = $data;
}
}
}
@@ -1120,6 +1177,8 @@ class Map extends Controller\AccessController {
/**
* get map log data
* @param \Base $f3
* @throws Exception
* @throws Exception\PathfinderException
*/
public function getLogData(\Base $f3){
$postData = (array)$f3->get('POST');

View File

@@ -509,6 +509,8 @@ class Route extends Controller\AccessController {
/**
* search multiple route between two systems
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function search($f3){
$requestData = (array)$f3->get('POST');

View File

@@ -18,6 +18,7 @@ class Signature extends Controller\AccessController {
* get signature data for systems
* -> return value of this is limited to a "SINGLE" system
* @param \Base $f3
* @throws \Exception
*/
public function getAll(\Base $f3){
$signatureData = [];
@@ -50,6 +51,7 @@ class Signature extends Controller\AccessController {
* save or update a full signature data set
* or save/update just single or multiple signature data
* @param \Base $f3
* @throws \Exception
*/
public function save(\Base $f3){
$requestData = $f3->get('POST');
@@ -221,6 +223,7 @@ class Signature extends Controller\AccessController {
/**
* delete signatures
* @param \Base $f3
* @throws \Exception
*/
public function delete(\Base $f3){
$signatureIds = $f3->get('POST.signatureIds');

View File

@@ -123,6 +123,7 @@ class Statistic extends Controller\AccessController {
* @param int $yearEnd
* @param int $weekEnd
* @return array
* @throws \Exception\PathfinderException
*/
protected function queryStatistic( CharacterModel $character, $typeId, $yearStart, $weekStart, $yearEnd, $weekEnd){
$data = [];
@@ -231,6 +232,7 @@ class Statistic extends Controller\AccessController {
/**
* get statistics data
* @param \Base $f3
* @throws \Exception
*/
public function getData(\Base $f3){
$postData = (array)$f3->get('POST');

View File

@@ -175,6 +175,7 @@ class System extends Controller\AccessController {
/**
* save a new system to a a map
* @param \Base $f3
* @throws \Exception
*/
public function save(\Base $f3){
$postData = (array)$f3->get('POST');
@@ -278,6 +279,7 @@ class System extends Controller\AccessController {
* get system log data from CCP API import
* system Kills, Jumps,....
* @param \Base $f3
* @throws \Exception
*/
public function graphData(\Base $f3){
$graphData = [];
@@ -329,6 +331,8 @@ class System extends Controller\AccessController {
* get system data for all systems within a constellation
* @param \Base $f3
* @param array $params
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function constellationData(\Base $f3, $params){
$return = (object) [];
@@ -363,6 +367,7 @@ class System extends Controller\AccessController {
/**
* set destination for specific systemIds
* @param \Base $f3
* @throws \Exception
*/
public function setDestination(\Base $f3){
$postData = (array)$f3->get('POST');
@@ -405,6 +410,8 @@ class System extends Controller\AccessController {
/**
* send Rally Point poke
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function pokeRally(\Base $f3){
$rallyData = (array)$f3->get('POST');
@@ -437,6 +444,7 @@ class System extends Controller\AccessController {
* delete systems and all its connections from map
* -> set "active" flag
* @param \Base $f3
* @throws \Exception
*/
public function delete(\Base $f3){
$mapId = (int)$f3->get('POST.mapId');
@@ -455,11 +463,20 @@ class System extends Controller\AccessController {
$map->getById($mapId);
if($map->hasAccess($activeCharacter)){
$newSystemModel = Model\BasicModel::getNew('SystemModel');
foreach($systemIds as $systemId){
if( $system = $map->getSystemById($systemId) ){
// check whether system should be deleted OR set "inactive"
if( $this->checkDeleteMode($map, $system) ){
$system->erase();
// delete log
// -> first set updatedCharacterId -> required for activity log
$system->updatedCharacterId = $activeCharacter;
$system->update();
// ... now get fresh object and delete..
$newSystemModel->getById( $system->id, 0);
$newSystemModel->erase();
$newSystemModel->reset();
}else{
// keep data -> set "inactive"
$system->setActive(false);

View File

@@ -42,10 +42,10 @@ class User extends Controller\Controller{
/**
* login a valid character
* @param Model\CharacterModel $characterModel
* @param string $browserTabId
* @return bool
* @throws Exception
*/
protected function loginByCharacter(Model\CharacterModel &$characterModel, string $browserTabId){
protected function loginByCharacter(Model\CharacterModel &$characterModel){
$login = false;
if($user = $characterModel->getUser()){
@@ -96,7 +96,7 @@ class User extends Controller\Controller{
// set temp character data ------------------------------------------------------------
// -> pass character data over for next http request (reroute())
$this->setTempCharacterData($characterModel->_id, $browserTabId);
$this->setTempCharacterData($characterModel->_id);
$login = true;
}
@@ -108,6 +108,8 @@ class User extends Controller\Controller{
* validate cookie character information
* -> return character data (if valid)
* @param \Base $f3
* @throws Exception
* @throws Exception\PathfinderException
*/
public function getCookieCharacter(\Base $f3){
$data = $f3->get('POST');
@@ -188,6 +190,7 @@ class User extends Controller\Controller{
/**
* delete the character log entry for the current active (main) character
* @param \Base $f3
* @throws Exception
*/
public function deleteLog(\Base $f3){
if($activeCharacter = $this->getCharacter()){
@@ -198,6 +201,8 @@ class User extends Controller\Controller{
/**
* log the current user out + clear character system log data
* @param \Base $f3
* @throws Exception
* @throws \ZMQSocketException
*/
public function logout(\Base $f3){
$this->logoutCharacter(false, true, true, true);
@@ -211,6 +216,7 @@ class User extends Controller\Controller{
* remote open ingame information window (character, corporation or alliance) Id
* -> the type is auto-recognized by CCP
* @param \Base $f3
* @throws Exception
*/
public function openIngameWindow(\Base $f3){
$data = $f3->get('POST');
@@ -241,6 +247,7 @@ class User extends Controller\Controller{
* -> a fresh user automatically generated on first login with a new character
* -> see SSO login
* @param \Base $f3
* @throws Exception
*/
public function saveAccount(\Base $f3){
$data = $f3->get('POST');
@@ -361,6 +368,8 @@ class User extends Controller\Controller{
/**
* delete current user account from DB
* @param \Base $f3
* @throws Exception
* @throws \ZMQSocketException
*/
public function deleteAccount(\Base $f3){
$data = $f3->get('POST.formData');

View File

@@ -34,7 +34,6 @@ class Sso extends Api\User{
const SESSION_KEY_SSO_ERROR = 'SESSION.SSO.ERROR';
const SESSION_KEY_SSO_STATE = 'SESSION.SSO.STATE';
const SESSION_KEY_SSO_FROM = 'SESSION.SSO.FROM';
const SESSION_KEY_SSO_TAB_ID = 'SESSION.SSO.TABID';
// error messages
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_SSO_URL" url. %s';
@@ -52,10 +51,10 @@ class Sso extends Api\User{
* redirect user to CCP SSO page and request authorization
* -> cf. Controller->getCookieCharacters() ( equivalent cookie based login)
* @param \Base $f3
* @throws \Exception\PathfinderException
*/
public function requestAdminAuthorization($f3){
// store browser tabId to be "targeted" after login
$f3->set(self::SESSION_KEY_SSO_TAB_ID, '');
$f3->set(self::SESSION_KEY_SSO_FROM, 'admin');
$scopes = self::getScopesByAuthType('admin');
@@ -66,13 +65,11 @@ class Sso extends Api\User{
* redirect user to CCP SSO page and request authorization
* -> cf. Controller->getCookieCharacters() ( equivalent cookie based login)
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function requestAuthorization($f3){
$params = $f3->get('GET');
$browserTabId = trim((string)$params['tabId']);
// store browser tabId to be "targeted" after login
$f3->set(self::SESSION_KEY_SSO_TAB_ID, $browserTabId);
if(
isset($params['characterId']) &&
@@ -108,7 +105,7 @@ class Sso extends Api\User{
$character->hasUserCharacter() &&
($character->isAuthorized() === 'OK')
){
$loginCheck = $this->loginByCharacter($character, $browserTabId);
$loginCheck = $this->loginByCharacter($character);
if($loginCheck){
// set "login" cookie
@@ -135,6 +132,7 @@ class Sso extends Api\User{
* @param \Base $f3
* @param array $scopes
* @param string $rootAlias
* @throws \Exception\PathfinderException
*/
private function rerouteAuthorization(\Base $f3, $scopes = [], $rootAlias = 'login'){
if( !empty( Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') ) ){
@@ -166,6 +164,8 @@ class Sso extends Api\User{
* callback handler for CCP SSO user Auth
* -> see requestAuthorization()
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function callbackAuthorization($f3){
$getParams = (array)$f3->get('GET');
@@ -178,8 +178,6 @@ class Sso extends Api\User{
$rootAlias = $f3->get(self::SESSION_KEY_SSO_FROM);
}
$browserTabId = (string)$f3->get(self::SESSION_KEY_SSO_TAB_ID) ;
if($f3->exists(self::SESSION_KEY_SSO_STATE)){
// check response and validate 'state'
if(
@@ -192,7 +190,6 @@ class Sso extends Api\User{
// clear 'state' for new next login request
$f3->clear(self::SESSION_KEY_SSO_STATE);
$f3->clear(self::SESSION_KEY_SSO_FROM);
$f3->clear(self::SESSION_KEY_SSO_TAB_ID);
$accessData = $this->getSsoAccessData($getParams['code']);
@@ -259,7 +256,7 @@ class Sso extends Api\User{
$characterModel = $userCharactersModel->getCharacter();
// login by character
$loginCheck = $this->loginByCharacter($characterModel, $browserTabId);
$loginCheck = $this->loginByCharacter($characterModel);
if($loginCheck){
// set "login" cookie
@@ -305,11 +302,12 @@ class Sso extends Api\User{
/**
* login by cookie name
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function login(\Base $f3){
$data = (array)$f3->get('GET');
$cookieName = empty($data['cookie']) ? '' : $data['cookie'];
$browserTabId = empty($data['tabId']) ? '' : $data['tabId'];
$character = null;
if( !empty($cookieName) ){
@@ -324,7 +322,7 @@ class Sso extends Api\User{
if( is_object($character)){
// login by character
$loginCheck = $this->loginByCharacter($character, $browserTabId);
$loginCheck = $this->loginByCharacter($character);
if($loginCheck){
// route to "map"
$f3->reroute(['map']);
@@ -343,6 +341,7 @@ class Sso extends Api\User{
* -> else try to refresh auth and get fresh "access_token"
* @param bool $authCode
* @return null|\stdClass
* @throws \Exception\PathfinderException
*/
public function getSsoAccessData($authCode){
$accessData = null;
@@ -362,6 +361,7 @@ class Sso extends Api\User{
* verify authorization code, and get an "access_token" data
* @param $authCode
* @return \stdClass
* @throws \Exception\PathfinderException
*/
protected function verifyAuthorizationCode($authCode){
$requestParams = [
@@ -377,6 +377,7 @@ class Sso extends Api\User{
* -> if "access_token" is expired, this function gets a fresh one
* @param $refreshToken
* @return \stdClass
* @throws \Exception\PathfinderException
*/
public function refreshAccessToken($refreshToken){
$requestParams = [
@@ -393,6 +394,7 @@ class Sso extends Api\User{
* OR by providing a valid "refresh_token"
* @param $requestParams
* @return \stdClass
* @throws \Exception\PathfinderException
*/
protected function requestAccessData($requestParams){
$verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint();
@@ -457,6 +459,7 @@ class Sso extends Api\User{
* -> if more character information is required, use ESI "characters" endpoints request instead
* @param $accessToken
* @return mixed|null
* @throws \Exception\PathfinderException
*/
public function verifyCharacterData($accessToken){
$verifyUserUrl = self::getVerifyUserEndpoint();
@@ -492,6 +495,7 @@ class Sso extends Api\User{
* get character data
* @param int $characterId
* @return object
* @throws \Exception
*/
public function getCharacterData($characterId){
$characterData = (object) [];
@@ -601,6 +605,7 @@ class Sso extends Api\User{
* get CCP SSO url from configuration file
* -> throw error if url is broken/missing
* @return string
* @throws \Exception\PathfinderException
*/
static function getSsoUrlRoot(){
$url = '';
@@ -630,6 +635,7 @@ class Sso extends Api\User{
/**
* get logger for SSO logging
* @return \Log
* @throws \Exception\PathfinderException
*/
static function getSSOLogger(){
return parent::getLogger('SSO');

View File

@@ -17,6 +17,7 @@ class Universe extends Controller {
/**
* Set up "Universe" Database
* @param \Base $f3
* @throws \Exception
*/
public function setupDB(\Base $f3){
$this->setupRegions($f3);
@@ -26,6 +27,7 @@ class Universe extends Controller {
/**
* get all regions from CCP and store region data
* @param \Base $f3
* @throws \Exception
*/
private function setupRegions(\Base $f3){
$this->getDB('UNIVERSE');
@@ -50,6 +52,7 @@ class Universe extends Controller {
/**
* get all constellations from CCP and store constellation data
* @param \Base $f3
* @throws \Exception
*/
private function setupConstellations(\Base $f3){
$this->getDB('UNIVERSE');

View File

@@ -63,6 +63,7 @@ class Controller {
* @param \Base $f3
* @param $params
* @return bool
* @throws \Exception\PathfinderException
*/
function beforeroute(\Base $f3, $params): bool {
// initiate DB connection
@@ -107,6 +108,7 @@ class Controller {
/**
* init new Session handler
* @param \Base $f3
*/
protected function initSession(\Base $f3){
$sessionCacheKey = $f3->get('SESSION_CACHE');
@@ -117,6 +119,7 @@ class Controller {
* @param $session
* @param $sid
* @return bool
* @throws \Exception\PathfinderException
*/
$onSuspect = function($session, $sid){
self::getLogger('SESSION_SUSPECT')->write( sprintf(
@@ -181,6 +184,7 @@ class Controller {
* set/update logged in cookie by character model
* -> store validation data in DB
* @param Model\CharacterModel $character
* @throws \Exception\PathfinderException
*/
protected function setLoginCookie(Model\CharacterModel $character){
if( $this->getCookieState() ){
@@ -237,6 +241,8 @@ class Controller {
* @param array $cookieData
* @param bool $checkAuthorization
* @return Model\CharacterModel[]
* @throws \Exception
* @throws \Exception\PathfinderException
*/
protected function getCookieCharacters($cookieData = [], $checkAuthorization = true){
$characters = [];
@@ -339,8 +345,8 @@ class Controller {
/**
* get current character data from session
* ->
* @return array
* @throws \Exception
*/
public function getSessionCharacterData(){
$data = [];
@@ -348,35 +354,16 @@ class Controller {
if($user = $this->getUser()){
$header = self::getRequestHeaders();
$requestedCharacterId = (int)$header['Pf-Character'];
$browserTabId = (string)$header['Pf-Tab-Id'];
$tempCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA);
if($this->getF3()->get('AJAX')){
if( !$this->getF3()->get('AJAX') ){
$requestedCharacterId = (int)$_COOKIE['old_char_id'];
if(!$requestedCharacterId){
$tempCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA);
if((int)$tempCharacterData['ID'] > 0){
$requestedCharacterId = (int)$tempCharacterData['ID'];
}
// _blank browser tab don´t have a $browserTabId jet..
// first Ajax call from that new tab with empty $requestedCharacterId -> bind to that new tab
if(
!empty($browserTabId) &&
$requestedCharacterId <= 0 &&
(int)$tempCharacterData['ID'] > 0 &&
empty($tempCharacterData['TAB_ID'])
){
$tempCharacterData['TAB_ID'] = $browserTabId;
// update tempCharacterData (SESSION)
$this->setTempCharacterData($tempCharacterData['ID'], $tempCharacterData['TAB_ID']);
}
if(
!empty($browserTabId) &&
!empty($tempCharacterData['TAB_ID']) &&
(int)$tempCharacterData['ID'] > 0 &&
$browserTabId === $tempCharacterData['TAB_ID']
){
$requestedCharacterId = (int)$tempCharacterData['ID'];
}
}elseif((int)$tempCharacterData['ID'] > 0){
$requestedCharacterId = (int)$tempCharacterData['ID'];
}
$data = $user->getSessionCharacterData($requestedCharacterId);
@@ -417,6 +404,7 @@ class Controller {
* get current user
* @param int $ttl
* @return Model\UserModel|null
* @throws \Exception
*/
public function getUser($ttl = 0){
$user = null;
@@ -442,14 +430,12 @@ class Controller {
/**
* set temp login character data (required during HTTP redirects on login)
* @param int $characterId
* @param string $browserTabId
* @throws \Exception
*/
protected function setTempCharacterData(int $characterId, string $browserTabId){
protected function setTempCharacterData(int $characterId){
if($characterId > 0){
$tempCharacterData = [
'ID' => $characterId,
'TAB_ID' => trim($browserTabId)
'ID' => $characterId
];
$this->getF3()->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $tempCharacterData);
}else{
@@ -463,6 +449,8 @@ class Controller {
* @param bool $deleteSession
* @param bool $deleteLog
* @param bool $deleteCookie
* @throws \Exception
* @throws \ZMQSocketException
*/
protected function logoutCharacter(bool $all = false, bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){
$sessionCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTERS);
@@ -581,6 +569,7 @@ class Controller {
/**
* get a custom userAgent string for API calls
* @return string
* @throws \Exception\PathfinderException
*/
protected function getUserAgent(){
$userAgent = '';
@@ -598,6 +587,7 @@ class Controller {
* -> on HTTP request -> render error page
* @param \Base $f3
* @return bool
* @throws \Exception\PathfinderException
*/
public function showError(\Base $f3){
@@ -817,6 +807,7 @@ class Controller {
* get the current registration status
* 0=registration stop |1=new registration allowed
* @return int
* @throws \Exception\PathfinderException
*/
static function getRegistrationStatus(){
return (int)Config::getPathfinderData('registration.status');
@@ -827,6 +818,7 @@ class Controller {
* -> set in pathfinder.ini
* @param string $type
* @return \Log|null
* @throws \Exception\PathfinderException
*/
static function getLogger($type){
return LogController::getLogger($type);
@@ -856,6 +848,7 @@ class Controller {
* health check for ICP socket -> ping request
* @param $ttl
* @param $load
* @throws \ZMQSocketException
*/
static function checkTcpSocket($ttl, $load){
(new Socket( Config::getSocketUri(), $ttl ))->sendData('healthCheck', $load);

View File

@@ -34,6 +34,7 @@ class LogController extends \Prefab {
/**
* get columns from ActivityLogModel that can be uses as counter
* @return array
* @throws \Exception
*/
protected function getActivityLogColumns(): array{
if(empty($this->activityLogColumns)){
@@ -56,6 +57,7 @@ class LogController extends \Prefab {
* -> this buffered data can be stored somewhere (e.g. DB) before HTTP response
* -> should be cleared afterwards!
* @param MapLog $log
* @throws \Exception
*/
public function push(MapLog $log){
$action = $log->getAction();
@@ -161,6 +163,7 @@ class LogController extends \Prefab {
* get Logger instance
* @param string $type
* @return \Log|null
* @throws \Exception\PathfinderException
*/
public static function getLogger($type){
$logFiles = Config::getPathfinderData('logfiles');

View File

@@ -15,10 +15,15 @@ class MapController extends AccessController {
/**
* @param \Base $f3
* @throws \Exception
* @throws \Exception\PathfinderException
*/
public function init(\Base $f3) {
$character = $this->getCharacter();
// characterId
$f3->set('tplCharacterId', $character->id);
// page title
$f3->set('tplPageTitle', $character->name . ' | ' . Config::getPathfinderData('name'));

View File

@@ -85,6 +85,7 @@ class Setup extends Controller {
'Model\ConstellationWormholeModel',
'Model\ConnectionModel',
'Model\ConnectionLogModel',
'Model\SystemSignatureModel',
'Model\ActivityLogModel',
@@ -139,6 +140,7 @@ class Setup extends Controller {
* @param \Base $f3
* @param array $params
* @return bool
* @throws \Exception\PathfinderException
*/
function beforeroute(\Base $f3, $params): bool {
// init dbLib class. Manages all DB connections
@@ -161,6 +163,7 @@ class Setup extends Controller {
/**
* @param \Base $f3
* @throws \Exception\PathfinderException
*/
public function afterroute(\Base $f3) {
// js view (file)
@@ -194,6 +197,7 @@ class Setup extends Controller {
* works as dispatcher for setup functions
* -> for security reasons all /setup "routes" are dispatched by GET params
* @param \Base $f3
* @throws \Exception
*/
public function init(\Base $f3){
$params = $f3->get('GET');
@@ -760,6 +764,7 @@ class Setup extends Controller {
* get default map config
* @param \Base $f3
* @return array
* @throws \Exception\PathfinderException
*/
protected function getMapsDefaultConfig(\Base $f3): array {
$matrix = \Matrix::instance();
@@ -1245,6 +1250,7 @@ class Setup extends Controller {
/**
* get Socket information (TCP (internal)), (WebSocket (clients))
* @return array
* @throws \ZMQSocketException
*/
protected function getSocketInformation(){
// $ttl for health check
@@ -1298,6 +1304,7 @@ class Setup extends Controller {
/** get indexed (cache) data information
* @return array
* @throws \Exception
*/
protected function getIndexData(){
// active DB and tables are required for obtain index data
@@ -1509,6 +1516,7 @@ class Setup extends Controller {
/**
* clear all character authentication (Cookie) data
* @param \Base $f3
* @throws \Exception
*/
protected function invalidateCookies(\Base $f3){
$this->getDB('PF');

View File

@@ -38,6 +38,7 @@ class CharacterUpdate {
* -> see deactivateLogData()
* >> php index.php "/cron/deleteLogData"
* @param \Base $f3
* @throws \Exception
*/
function deleteLogData(\Base $f3){
DB\Database::instance()->getDB('PF');
@@ -80,6 +81,7 @@ class CharacterUpdate {
* clean up outdated character data e.g. kicked until status
* >> php index.php "/cron/cleanUpCharacterData"
* @param \Base $f3
* @throws \Exception
*/
function cleanUpCharacterData(\Base $f3){
DB\Database::instance()->getDB('PF');
@@ -111,6 +113,7 @@ class CharacterUpdate {
* authentication data is used for cookie based login
* >> php index.php "/cron/deleteAuthenticationData"
* @param \Base $f3
* @throws \Exception
*/
function deleteAuthenticationData($f3){
DB\Database::instance()->getDB('PF');

View File

@@ -22,6 +22,7 @@ class MapUpdate {
* deactivate all "private" maps whose lifetime is over
* >> php index.php "/cron/deactivateMapData"
* @param \Base $f3
* @throws \Exception\PathfinderException
*/
function deactivateMapData(\Base $f3){
$privateMapLifetime = (int)Config::getMapsDefaultConfig('private.lifetime');
@@ -45,6 +46,7 @@ class MapUpdate {
* delete all deactivated maps
* >> php index.php "/cron/deleteMapData"
* @param \Base $f3
* @throws \Exception
*/
function deleteMapData(\Base $f3){
$pfDB = DB\Database::instance()->getDB('PF');
@@ -82,6 +84,7 @@ class MapUpdate {
* delete expired EOL connections
* >> php index.php "/cron/deleteEolConnections"
* @param \Base $f3
* @throws \Exception
*/
function deleteEolConnections(\Base $f3){
$eolExpire = (int)$f3->get('PATHFINDER.CACHE.EXPIRE_CONNECTIONS_EOL');
@@ -125,6 +128,7 @@ class MapUpdate {
* delete expired WH connections after max lifetime for wormholes is reached
* >> php index.php "/cron/deleteExpiredConnections"
* @param \Base $f3
* @throws \Exception
*/
function deleteExpiredConnections(\Base $f3){
$whExpire = (int)$f3->get('PATHFINDER.CACHE.EXPIRE_CONNECTIONS_WH');

View File

@@ -116,6 +116,7 @@ class Database extends \Prefab {
* @param string $password
* @param string $alias
* @return SQL|null
* @throws \Exception\PathfinderException
*/
protected function connect($dns, $name, $user, $password, $alias){
$db = null;
@@ -285,6 +286,7 @@ class Database extends \Prefab {
/**
* get logger for DB logging
* @return \Log
* @throws \Exception\PathfinderException
*/
static function getLogger(){
return LogController::getLogger('ERROR');

View File

@@ -67,6 +67,7 @@ class Monolog extends \Prefab {
* -> this buffered data can be stored/logged somewhere (e.g. DB/file) at any time
* -> should be cleared afterwards!
* @param Logging\AbstractLog $log
* @throws \Exception
*/
public function push(Logging\AbstractLog $log){
// check whether $log should be "grouped" by common handlers

View File

@@ -24,6 +24,7 @@ class CcpClient extends \Prefab {
* get ApiClient instance
* @param \Base $f3
* @return ApiClient|null
* @throws \Exception\PathfinderException
*/
protected function getClient(\Base $f3){
$client = null;
@@ -43,6 +44,7 @@ class CcpClient extends \Prefab {
/**
* @return string
* @throws \Exception\PathfinderException
*/
protected function getUserAgent(){
$userAgent = '';
@@ -68,6 +70,7 @@ class CcpClient extends \Prefab {
* @param $name
* @param $arguments
* @return array|mixed
* @throws \Exception\PathfinderException
*/
public function __call($name, $arguments){
$return = [];

View File

@@ -208,6 +208,7 @@ class Config extends \Prefab {
/**
* get SMTP config values
* @return \stdClass
* @throws Exception\PathfinderException
*/
static function getSMTPConfig(): \stdClass{
$config = new \stdClass();
@@ -252,6 +253,7 @@ class Config extends \Prefab {
* get email for notifications by hive key
* @param $key
* @return mixed
* @throws Exception\PathfinderException
*/
static function getNotificationMail($key){
return self::getPathfinderData('notification' . ($key ? '.' . $key : ''));
@@ -262,6 +264,7 @@ class Config extends \Prefab {
* -> read from pathfinder.ini
* @param string $mapType
* @return mixed
* @throws Exception\PathfinderException
*/
static function getMapsDefaultConfig($mapType = ''){
if( $mapConfig = self::getPathfinderData('map' . ($mapType ? '.' . $mapType : '')) ){

View File

@@ -68,6 +68,7 @@ abstract class AbstractCharacterLog extends AbstractChannelLog{
/**
* get character thumbnailUrl
* @return string
* @throws \Exception\PathfinderException
*/
protected function getThumbUrl(): string {
$url = '';

View File

@@ -426,6 +426,7 @@ abstract class AbstractLog implements LogInterface {
/**
* get __construct() parameters for ZMQHandler() call
* @return array
* @throws \ZMQSocketException
*/
protected function getHandlerParamsZMQ(): array {
$params = [];

View File

@@ -60,6 +60,7 @@ class LogCollection extends AbstractLog {
/**
* add a new log object to this collection
* @param AbstractLog $log
* @throws \Exception
*/
public function addLog(AbstractLog $log){
if(!$this->collection->contains($log)){
@@ -113,6 +114,7 @@ class LogCollection extends AbstractLog {
/**
* @param string $tag
* @throws \Exception
*/
public function setTag(string $tag){
$currentTag = parent::getTag();
@@ -147,6 +149,7 @@ class LogCollection extends AbstractLog {
/**
* @return string
* @throws \Exception
*/
public function getChannelName() : string{
return $this->getPrimaryLog()->getChannelName();
@@ -154,6 +157,7 @@ class LogCollection extends AbstractLog {
/**
* @return string
* @throws \Exception
*/
public function getLevel() : string{
return $this->getPrimaryLog()->getLevel();
@@ -161,6 +165,7 @@ class LogCollection extends AbstractLog {
/**
* @return bool
* @throws \Exception
*/
public function hasBuffer() : bool{
return $this->getPrimaryLog()->hasBuffer();
@@ -168,6 +173,7 @@ class LogCollection extends AbstractLog {
/**
* @return array
* @throws \Exception
*/
public function getTempData() : array{
return $this->getPrimaryLog()->getTempData();

View File

@@ -37,6 +37,7 @@ class RallyLog extends AbstractCharacterLog{
/**
* @return string
* @throws \Exception\PathfinderException
*/
protected function getThumbUrl() : string{
$url = '';

View File

@@ -179,6 +179,7 @@ abstract class AbstractSlackWebhookHandler extends Handler\AbstractProcessingHan
* @param array $attachment
* @param array $characterData
* @return array
* @throws \Exception\PathfinderException
*/
protected function setAuthor(array $attachment, array $characterData): array {
if( !empty($characterData['id']) && !empty($characterData['name'])){

View File

@@ -15,6 +15,7 @@ class SlackMapWebhookHandler extends AbstractSlackWebhookHandler {
/**
* @param array $record
* @return array
* @throws \Exception\PathfinderException
*/
protected function getSlackData(array $record) : array{
$postData = parent::getSlackData($record);

View File

@@ -15,6 +15,7 @@ class SlackRallyWebhookHandler extends AbstractSlackWebhookHandler {
/**
* @param array $record
* @return array
* @throws \Exception\PathfinderException
*/
protected function getSlackData(array $record) : array{
$postData = parent::getSlackData($record);

View File

@@ -87,9 +87,10 @@ class Socket {
}
/**
* @param $task
* @param string $task
* @param string $load
* @return bool|string
* @throws \ZMQSocketException
*/
public function sendData(string $task, $load = ''){
$response = false;

View File

@@ -100,6 +100,7 @@ class Web extends \Web {
* @param array $additionalOptions
* @param int $retryCount request counter for failed call
* @return array|FALSE|mixed
* @throws \Exception\PathfinderException
*/
public function request($url,array $options = null, $additionalOptions = [], $retryCount = 0 ) {
$f3 = \Base::instance();

View File

@@ -52,6 +52,7 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn
* @param $key
* @param $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_notDry($key, $val): bool {
$valid = true;
@@ -92,7 +93,7 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn
/**
* validates all required columns of this class
* @return bool
* @throws \Exception\ValidationException
* @throws \Exception\DatabaseException
*/
public function isValid(): bool {
if($valid = parent::isValid()){

View File

@@ -57,6 +57,7 @@ class AllianceModel extends BasicModel {
/**
* get all maps for this alliance
* @return array|mixed
* @throws \Exception\PathfinderException
*/
public function getMaps(){
$maps = [];

View File

@@ -822,6 +822,7 @@ abstract class BasicModel extends \DB\Cortex {
* debug log function
* @param string $text
* @param string $type
* @throws \Exception\PathfinderException
*/
public static function log($text, $type = 'DEBUG'){
Controller\LogController::getLogger($type)->write($text);

View File

@@ -139,25 +139,25 @@ class CharacterLogModel extends BasicModel {
*/
public function getData(){
$logData = (object) [];
$logData->system = (object) [];
$logData->system->id = (int)$this->systemId;
$logData->system->name = $this->systemName;
$logData = (object) [];
$logData->system = (object) [];
$logData->system->id = (int)$this->systemId;
$logData->system->name = $this->systemName;
$logData->ship = (object) [];
$logData->ship->typeId = (int)$this->shipTypeId;
$logData->ship->typeName = $this->shipTypeName;
$logData->ship->id = $this->shipId;
$logData->ship->name = $this->shipName;
$logData->ship->mass = $this->shipMass;
$logData->ship = (object) [];
$logData->ship->typeId = (int)$this->shipTypeId;
$logData->ship->typeName = $this->shipTypeName;
$logData->ship->id = $this->shipId;
$logData->ship->name = $this->shipName;
$logData->ship->mass = $this->shipMass;
$logData->station = (object) [];
$logData->station->id = (int)$this->stationId;
$logData->station->name = $this->stationName;
$logData->station = (object) [];
$logData->station->id = (int)$this->stationId;
$logData->station->name = $this->stationName;
$logData->structure = (object) [];
$logData->structure->id = (int)$this->structureId;
$logData->structure->name = $this->structureName;
$logData->structure = (object) [];
$logData->structure->id = (int)$this->structureId;
$logData->structure->name = $this->structureName;
return $logData;
}
@@ -166,6 +166,7 @@ class CharacterLogModel extends BasicModel {
* setter for systemId
* @param int $systemId
* @return int
* @throws \Exception
*/
public function set_systemId($systemId){
if($systemId > 0){
@@ -219,6 +220,7 @@ class CharacterLogModel extends BasicModel {
/**
* update session data for active character
* @param int $systemId
* @throws \Exception
*/
protected function updateCharacterSessionLocation(int $systemId){
$controller = new Controller();

View File

@@ -471,6 +471,7 @@ class CharacterModel extends BasicModel {
/**
* get ESI API "access_token" from OAuth
* @return bool|string
* @throws \Exception\PathfinderException
*/
public function getAccessToken(){
$accessToken = false;
@@ -541,6 +542,7 @@ class CharacterModel extends BasicModel {
* checks whether this character is authorized to log in
* -> check corp/ally whitelist config (pathfinder.ini)
* @return bool
* @throws \Exception\PathfinderException
*/
public function isAuthorized(){
$authStatus = 'UNKNOWN';
@@ -596,6 +598,7 @@ class CharacterModel extends BasicModel {
/**
* get pathfinder roleId
* @return int
* @throws \Exception\PathfinderException
*/
public function requestRoleId(){
$roleId = self::ROLES['MEMBER'];
@@ -614,6 +617,7 @@ class CharacterModel extends BasicModel {
/**
* request all corporation roles granted to this character
* @return array
* @throws \Exception\PathfinderException
*/
protected function requestRoles(){
$rolesData = [];
@@ -655,6 +659,7 @@ class CharacterModel extends BasicModel {
* -> API request for character log data
* @param array $additionalOptions (optional) request options for cURL request
* @return CharacterModel
* @throws \Exception
*/
public function updateLog($additionalOptions = []){
$deleteLog = false;
@@ -874,6 +879,7 @@ class CharacterModel extends BasicModel {
/**
* update character data from CCPs ESI API
* @return array (some status messages)
* @throws \Exception
*/
public function updateFromESI(){
$status = [];
@@ -938,6 +944,7 @@ class CharacterModel extends BasicModel {
* get mapModel by id and check if user has access
* @param $mapId
* @return MapModel|null
* @throws \Exception
*/
public function getMap($mapId){
/**
@@ -957,6 +964,7 @@ class CharacterModel extends BasicModel {
/**
* get all accessible map models for this character
* @return MapModel[]
* @throws \Exception\PathfinderException
*/
public function getMaps(){
$this->filter(

View File

@@ -57,6 +57,7 @@ class CharacterStatusModel extends BasicModel {
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -0,0 +1,84 @@
<?php
/**
* Created by PhpStorm.
* User: exodu
* Date: 05.11.2017
* Time: 17:51
*/
namespace Model;
use DB\SQL\Schema;
class ConnectionLogModel extends BasicModel {
protected $table = 'connection_log';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 1,
'index' => true
],
'connectionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\ConnectionModel',
'constraint' => [
[
'table' => 'connection',
'on-delete' => 'CASCADE'
]
]
],
'shipTypeId' => [
'type' => Schema::DT_INT,
'index' => true
],
'shipTypeName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shipMass' => [
'type' => Schema::DT_FLOAT,
'nullable' => false,
'default' => 0
],
'characterId' => [
'type' => Schema::DT_INT,
'index' => true
],
'characterName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
/**
* get connection log data
* @return \stdClass
*/
public function getData() : \stdClass {
$logData = (object) [];
$logData->id = $this->id;
$logData->connection = (object) [];
$logData->connection->id = $this->get('connectionId', true);
$logData->ship = (object) [];
$logData->ship->typeId = (int)$this->shipTypeId;
$logData->ship->typeName = $this->shipTypeName;
$logData->ship->mass = $this->shipMass;
$logData->created = (object) [];
$logData->created->created = strtotime($this->created);
$logData->created->character = (object) [];
$logData->created->character->id = $this->characterId;
$logData->created->character->name = $this->characterName;
return $logData;
}
}

View File

@@ -74,6 +74,9 @@ class ConnectionModel extends AbstractMapTrackingModel {
],
'signatures' => [
'has-many' => ['Model\SystemSignatureModel', 'connectionId']
],
'connectionLog' => [
'has-many' => ['Model\ConnectionLogModel', 'connectionId']
]
];
@@ -101,20 +104,21 @@ class ConnectionModel extends AbstractMapTrackingModel {
}
/**
* get connection data as array
* get connection data
* @param bool $addSignatureData
* @param bool $addLogData
* @return \stdClass
*/
public function getData($addSignatureData = false){
public function getData($addSignatureData = false, $addLogData = false){
$connectionData = (object) [];
$connectionData->id = $this->id;
$connectionData->source = $this->source->id;
$connectionData->target = $this->target->id;
$connectionData->scope = $this->scope;
$connectionData->type = $this->type;
$connectionData->updated = strtotime($this->updated);
$connectionData->created = strtotime($this->created);
$connectionData->eolUpdated = strtotime($this->eolUpdated);
$connectionData->id = $this->id;
$connectionData->source = $this->source->id;
$connectionData->target = $this->target->id;
$connectionData->scope = $this->scope;
$connectionData->type = $this->type;
$connectionData->updated = strtotime($this->updated);
$connectionData->created = strtotime($this->created);
$connectionData->eolUpdated = strtotime($this->eolUpdated);
if($addSignatureData){
if( !empty($signaturesData = $this->getSignaturesData()) ){
@@ -122,6 +126,12 @@ class ConnectionModel extends AbstractMapTrackingModel {
}
}
if($addLogData){
if( !empty($logsData = $this->getLogsData()) ){
$connectionData->logs = $logsData;
}
}
return $connectionData;
}
@@ -193,6 +203,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
/**
* check whether this model is valid or not
* @return bool
* @throws \Exception\DatabaseException
*/
public function isValid(): bool {
if($valid = parent::isValid()){
@@ -218,6 +229,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
* @param ConnectionModel $self
* @param $pkeys
* @return bool
* @throws \Exception\DatabaseException
*/
public function beforeInsertEvent($self, $pkeys){
// check for "default" connection type and add them if missing
@@ -269,6 +281,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
/**
* @param string $action
* @return Logging\LogInterface
* @throws \Exception\PathfinderException
*/
public function newLog($action = ''): Logging\LogInterface{
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());
@@ -330,11 +343,29 @@ class ConnectionModel extends AbstractMapTrackingModel {
return $signatures;
}
/**
* get all jump logs that are connected with this connection
* @return array|mixed
*/
public function getLogs(){
$logs = [];
$this->filter('connectionLog', [
'active = :active',
':active' => 1
]);
if($this->connectionLog){
$logs = $this->connectionLog;
}
return $logs;
}
/**
* get all signature data linked to this connection
* @return array
*/
public function getSignaturesData(){
public function getSignaturesData() : array {
$signaturesData = [];
$signatures = $this->getSignatures();
@@ -345,6 +376,36 @@ class ConnectionModel extends AbstractMapTrackingModel {
return $signaturesData;
}
/**
* get all connection log data linked to this connection
* @return array
*/
public function getLogsData() : array{
$logsData = [];
$logs = $this->getLogs();
foreach($logs as $log){
$logsData[] = $log->getData();
}
return $logsData;
}
public function logMass(CharacterLogModel $characterLog){
if( !$characterLog->dry() ){
$log = $this->rel('connectionLog');
$log->shipTypeId = $characterLog->shipTypeId;
$log->shipTypeName = $characterLog->shipTypeName;
$log->shipMass = $characterLog->shipMass;
$log->characterId = $characterLog->characterId->_id;
$log->characterName = $characterLog->characterId->name;
$log->connectionId = $this;
$log->save();
}
return $this;
}
/**
* overwrites parent
* @param null $db

View File

@@ -66,6 +66,7 @@ class ConnectionScopeModel extends BasicModel{
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -128,6 +128,7 @@ class CorporationModel extends BasicModel {
/**
* get all maps for this corporation
* @return MapModel[]
* @throws \Exception\PathfinderException
*/
public function getMaps(){
$maps = [];

View File

@@ -181,6 +181,8 @@ class MapModel extends AbstractMapTrackingModel {
* get map data
* -> this includes system and connection data as well!
* @return \stdClass
* @throws PathfinderException
* @throws \Exception
*/
public function getData(){
// check if there is cached data
@@ -294,6 +296,7 @@ class MapModel extends AbstractMapTrackingModel {
* @param string $key
* @param string $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_name(string $key, string $val): bool {
$valid = true;
@@ -309,6 +312,7 @@ class MapModel extends AbstractMapTrackingModel {
* @param string $key
* @param string $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_slackWebHookURL(string $key, string $val): bool {
$valid = true;
@@ -401,6 +405,7 @@ class MapModel extends AbstractMapTrackingModel {
* -> check for "inactive" systems on this map first!
* @param int $systemId
* @return SystemModel
* @throws \Exception
*/
public function getNewSystem($systemId){
// check for "inactive" system
@@ -510,6 +515,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* get all system data for all systems in this map
* @return \stdClass[]
* @throws \Exception
*/
public function getSystemData(){
$systemData = [];
@@ -545,10 +551,11 @@ class MapModel extends AbstractMapTrackingModel {
/**
* get all connections in this map
* @param null $connectionIds
* @param string $scope
* @return ConnectionModel[]
*/
public function getConnections($scope = ''){
public function getConnections($connectionIds = null, $scope = ''){
$connections = [];
$query = [
@@ -561,6 +568,11 @@ class MapModel extends AbstractMapTrackingModel {
$query[':scope'] = $scope;
}
if(!empty($connectionIds)){
$query[0] .= ' AND id IN (?)';
$query[] = $connectionIds;
}
$this->filter('connections', $query);
if($this->connections){
@@ -591,6 +603,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* set map access for an object (character, corporation or alliance)
* @param $obj
* @throws \Exception
*/
public function setAccess($obj){
@@ -689,6 +702,7 @@ class MapModel extends AbstractMapTrackingModel {
* checks whether a character has access to this map or not
* @param CharacterModel $characterModel
* @return bool
* @throws PathfinderException
*/
public function hasAccess(CharacterModel $characterModel){
$hasAccess = false;
@@ -834,6 +848,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* @param string $action
* @return Logging\LogInterface
* @throws PathfinderException
*/
public function newLog($action = ''): Logging\LogInterface{
$logChannelData = $this->getLogChannelData();
@@ -901,6 +916,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* check if "activity logging" is enabled for this map type
* @return bool
* @throws PathfinderException
*/
public function isActivityLogEnabled(): bool {
return $this->logActivity && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_activity_enabled'];
@@ -909,6 +925,7 @@ class MapModel extends AbstractMapTrackingModel {
/**
* check if "history logging" is enabled for this map type
* @return bool
* @throws PathfinderException
*/
public function isHistoryLogEnabled(): bool {
return $this->logHistory && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_history_enabled'];
@@ -946,6 +963,7 @@ class MapModel extends AbstractMapTrackingModel {
* check if "E-Mail" Log is enabled for this map
* @param string $type
* @return bool
* @throws PathfinderException
*/
public function isMailSendEnabled(string $type): bool{
$enabled = false;
@@ -1005,6 +1023,7 @@ class MapModel extends AbstractMapTrackingModel {
* @param string $type
* @param bool $addJson
* @return \stdClass
* @throws PathfinderException
*/
public function getSMTPConfig(string $type, bool $addJson = true): \stdClass{
$config = Config::getSMTPConfig();
@@ -1141,6 +1160,8 @@ class MapModel extends AbstractMapTrackingModel {
* get all active characters (with active log)
* grouped by systems
* @return \stdClass
* @throws PathfinderException
* @throws \Exception
*/
public function getUserData(){

View File

@@ -59,6 +59,7 @@ class MapScopeModel extends BasicModel{
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -87,6 +87,7 @@ class MapTypeModel extends BasicModel{
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -410,6 +410,7 @@ class SystemModel extends AbstractMapTrackingModel {
/**
* @param string $action
* @return Logging\LogInterface
* @throws \Exception\PathfinderException
*/
public function newLog($action = ''): Logging\LogInterface{
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());
@@ -567,6 +568,7 @@ class SystemModel extends AbstractMapTrackingModel {
* -> send to an Email
* @param array $rallyData
* @param CharacterModel $characterModel
* @throws \Exception\PathfinderException
*/
public function sendRallyPoke(array $rallyData, CharacterModel $characterModel){
// rally log needs at least one handler to be valid

View File

@@ -169,6 +169,7 @@ class SystemSignatureModel extends AbstractMapTrackingModel {
* @param string $key
* @param string $val
* @return bool
* @throws \Exception\ValidationException
*/
protected function validate_name(string $key, string $val): bool {
$valid = true;
@@ -182,6 +183,7 @@ class SystemSignatureModel extends AbstractMapTrackingModel {
/**
* @param string $action
* @return Logging\LogInterface
* @throws \Exception\PathfinderException
*/
public function newLog($action = ''): Logging\LogInterface{
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());

View File

@@ -83,6 +83,7 @@ class SystemStatusModel extends BasicModel {
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -45,6 +45,7 @@ class SystemTypeModel extends BasicModel {
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);

View File

@@ -54,4 +54,13 @@ class ConstellationModel extends BasicUniverseModel {
]
];
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
}
}

View File

@@ -33,4 +33,13 @@ class RegionModel extends BasicUniverseModel {
'has-many' => ['Model\Universe\ConstellationModel', 'regionId']
],
];
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
}
}

View File

@@ -49,6 +49,7 @@ class UserModel extends BasicModel {
* -> ! caution ! this function returns sensitive data! (e.g. email,..)
* -> user getSimpleData() for faster performance and public user data
* @return \stdClass
* @throws Exception
*/
public function getData(){
@@ -93,6 +94,7 @@ class UserModel extends BasicModel {
* @param UserModel $self
* @param $pkeys
* @return bool
* @throws Exception\PathfinderException
* @throws Exception\RegistrationException
*/
public function beforeInsertEvent($self, $pkeys){
@@ -135,6 +137,7 @@ class UserModel extends BasicModel {
/**
* checks whether user has a valid email address and pathfinder has a valid SMTP config
* @return bool
* @throws Exception\PathfinderException
*/
protected function isMailSendEnabled() : bool{
return Config::isValidSMTPConfig($this->getSMTPConfig());
@@ -143,6 +146,7 @@ class UserModel extends BasicModel {
/**
* get SMTP config for this user
* @return \stdClass
* @throws Exception\PathfinderException
*/
protected function getSMTPConfig() : \stdClass{
$config = Config::getSMTPConfig();
@@ -155,6 +159,7 @@ class UserModel extends BasicModel {
* @param string $key
* @param string $val
* @return bool
* @throws Exception\ValidationException
*/
protected function validate_name(string $key, string $val): bool {
$valid = true;
@@ -173,6 +178,7 @@ class UserModel extends BasicModel {
* @param string $key
* @param string $val
* @return bool
* @throws Exception\ValidationException
*/
protected function validate_email(string $key, string $val): bool {
$valid = true;
@@ -207,6 +213,7 @@ class UserModel extends BasicModel {
* @param int $characterId
* @param bool $objectCheck
* @return array
* @throws Exception
*/
public function getSessionCharacterData($characterId = 0, $objectCheck = true){
$data = [];
@@ -289,6 +296,7 @@ class UserModel extends BasicModel {
* -> EITHER - the current active one for the current user
* -> OR - get the first active one
* @return null|CharacterModel
* @throws Exception
*/
public function getActiveCharacter(){
$activeCharacter = null;

View File

@@ -59,47 +59,27 @@ class WormholeModel extends BasicModel {
*/
protected $addStaticFields = false;
/**
* format mass values
* - no decimal separator
* - char '.' for thousands separator
* @param $value
* @return string
*/
static function formatMassValue($value){
return number_format( $value, 0, '', '.' );
}
/**
* get wormhole data as object
* @return object
*/
public function getData(){
$systemStaticData = (object) [];
$systemStaticData->name = $this->name;
$systemStaticData->security = $this->security;
// total (max) available wormhole mass
$systemStaticData->massTotal = (object) [];
$systemStaticData->massTotal->value = $this->massTotal;
$systemStaticData->massTotal->format = self::formatMassValue($this->massTotal) . ' Kg';
$systemStaticData->massTotal = $this->massTotal;
// individual jump mass (max) per jump
$systemStaticData->massIndividual = (object) [];
$systemStaticData->massIndividual->value = $this->massIndividual;
$systemStaticData->massIndividual->format = self::formatMassValue($this->massIndividual) . ' Kg';
$systemStaticData->massIndividual = $this->massIndividual;
// lifetime (max) for this wormhole
$systemStaticData->maxStableTime = (object) [];
$systemStaticData->maxStableTime->value = $this->maxStableTime;
$systemStaticData->maxStableTime->format = $this->maxStableTime . ' h';
$systemStaticData->maxStableTime = $this->maxStableTime;
// mass regeneration value per day
if($this->massRegeneration > 0){
$systemStaticData->massRegeneration = (object) [];
$systemStaticData->massRegeneration->value = $this->massRegeneration;
$systemStaticData->massRegeneration->format = self::formatMassValue($this->massRegeneration) . ' Kg/day';
$systemStaticData->massRegeneration = $this->massRegeneration;
}
return $systemStaticData;

View File

@@ -3,7 +3,7 @@
[PATHFINDER]
NAME = Pathfinder
; installed version (used for CSS/JS cache busting)
VERSION = v1.3.0
VERSION = v1.3.1
; contact information [optional]
CONTACT = https://github.com/exodus4d
; public contact email [optional]

View File

@@ -21,7 +21,7 @@
}],
"require": {
"php-64bit": ">=7.0",
"ext-curl": ">=7.0",
"ext-curl": "*",
"ext-zmq": ">=1.1.3",
"react/zmq": "0.3.*",
"monolog/monolog": "1.*",

View File

@@ -21,7 +21,7 @@
}],
"require": {
"php-64bit": ">=7.0",
"ext-curl": ">=7.0",
"ext-curl": "*",
"ext-zmq": ">=1.1.3",
"react/zmq": "0.3.*",
"monolog/monolog": "1.*",

View File

@@ -177,12 +177,14 @@ let CONF = {
JS: {
UGLIFY: options.hasOwnProperty('jsUglify') ? options.jsUglify === 'true': undefined,
SOURCEMAPS: options.hasOwnProperty('jsSourcemaps') ? options.jsSourcemaps === 'true': undefined,
GZIP: options.hasOwnProperty('jsGzip') ? options.jsGzip === 'true': undefined,
BROTLI: options.hasOwnProperty('jsBrotli') ? options.jsBrotli === 'true': undefined
},
CSS: {
SOURCEMAPS: options.hasOwnProperty('cssSourcemaps') ? options.cssSourcemaps === 'true': undefined,
GZIP: options.hasOwnProperty('cssGzip') ? options.cssGzip === 'true': undefined,
BROTLI: options.hasOwnProperty('cssBrotli') ? options.cssBrotli === 'true': undefined
},
GZIP: options.hasOwnProperty('gzip') ? options.gzip === 'true': undefined,
BROTLI: options.hasOwnProperty('brotli') ? options.brotli === 'true': undefined,
DEBUG: false
};
@@ -271,18 +273,21 @@ let printHelp = () => {
${chalk.cyan('tasks:')}
${chalk.gray('help')} This view
${chalk.gray('default')} Development environment. Working with row src files and file watcher, default:
${chalk.gray('')} ${chalk.gray('--jsUglify=false --jsSourcemaps=false --cssSourcemaps=false --gzip=false --brotli=false')}
${chalk.gray('')} ${chalk.gray('--jsUglify=false --jsSourcemaps=false --cssSourcemaps=false --jsGzip=false --cssGzip=false --jsBrotli=false --cssBrotli=false')}
${chalk.gray('production')} Production build. Concat and uglify static resources, default:
${chalk.gray('')} ${chalk.gray('--jsUglify=true --jsSourcemaps=true --cssSourcemaps=true --gzip=true --brotli=true')}
${chalk.gray('')} ${chalk.gray('--jsUglify=true --jsSourcemaps=true --cssSourcemaps=true --jsGzip=true --cssGzip=true --jsBrotli=true --cssBrotli=true')}
${chalk.cyan('options:')}
${chalk.gray('--tag')} Set build version. ${chalk.gray('default: --tag="v1.2.4" -> dest path: public/js/v1.2.4')}
${chalk.gray('--jsUglify')} Set js uglification. ${chalk.gray('(true || false)')}
${chalk.gray('--jsSourcemaps')} Set js sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--cssSourcemaps')} Set CSS sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--gzip')} Set "gzip" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--brotli')} Set "brotli" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--debug')} Set debug mode (more output). ${chalk.gray('(true || false)')}
${chalk.gray('--tag')} Set build version. ${chalk.gray('default: --tag="v1.2.4" -> dest path: public/js/v1.2.4')}
${chalk.gray('--jsUglify')} Set js uglification. ${chalk.gray('(true || false)')}
${chalk.gray('--jsSourcemaps')} Set js sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--jsGzip')} Set js "gzip" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--jsBrotli')} Set js "brotli" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--cssSourcemaps')} Set CSS sourcemaps generation. ${chalk.gray('(true || false)')}
${chalk.gray('--cssGzip')} Set CSS "gzip" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--cssBrotli')} Set CSS "brotli" compression mode. ${chalk.gray('(true || false)')}
${chalk.gray('--debug')} Set debug mode (more output). ${chalk.gray('(true || false)')}
`)
.log(chalk.cyan('='.repeat(cliBoxLength)))
.log('');
@@ -295,8 +300,8 @@ let printJsSummary = () => {
let tableHead = trackTable.cols;
let byteCols = [1,3,6,9,12];
let percentCols = [2, 5, 8, 10];
let sortCol = (CONF.BROTLI || CONF.GZIP || CONF.JS.UGLIFY) ? 10 : CONF.JS.SOURCEMAPS ? 3 : 1;
let refAllCol = CONF.BROTLI ? 9 : CONF.GZIP ? 6 : CONF.JS.UGLIFY ? 3 : CONF.JS.SOURCEMAPS ? 3 : 1;
let sortCol = (CONF.JS.BROTLI || CONF.CSS.BROTLI || CONF.JS.GZIP || CONF.CSS.GZIP || CONF.JS.UGLIFY) ? 10 : CONF.JS.SOURCEMAPS ? 3 : 1;
let refAllCol = (CONF.JS.BROTLI || CONF.CSS.BROTLI) ? 9 : (CONF.JS.GZIP || CONF.CSS.GZIP) ? 6 : CONF.JS.UGLIFY ? 3 : CONF.JS.SOURCEMAPS ? 3 : 1;
let highLightSections = {src_percent: [], gzip_percent: [], brotli_percent: [], all_percent: []};
let highLightRow = {
success: JSON.parse(JSON.stringify(highLightSections)),
@@ -446,7 +451,7 @@ let printJsSummary = () => {
table.removeColumn(12);
table.removeColumn(11);
if(!CONF.BROTLI && !CONF.GZIP){
if(!CONF.JS.BROTLI && !CONF.CSS.BROTLI && !CONF.JS.GZIP && !CONF.CSS.GZIP){
table.removeColumn(10);
table.removeColumn(9);
@@ -559,16 +564,31 @@ gulp.task('task:diffJS', () => {
.pipe(gulp.dest(PATH.JS.DIST_BUILD, {overwrite: false}));
});
gulp.task('task:gzipAssets', () => {
let filterGzip = filter(file => CONF.GZIP);
let fileExt = ['js', 'css'];
let srcModules = [
PATH.ASSETS.DIST +'/**/*.{' + fileExt.join(',') + '}',
'!' + PATH.ASSETS.DIST + '/js/' + CONF.TAG + '{,/**/*}'
];
/**
* get assets filter options e.g. for gZip or Brotli assets
* @param compression
* @param fileExt
* @returns {{srcModules: *[], fileFilter}}
*/
let getAssetFilterOptions = (compression, fileExt) => {
return {
srcModules: [
PATH.ASSETS.DIST +'/**/*.' + fileExt,
'!' + PATH.ASSETS.DIST + '/js/' + CONF.TAG + '{,/**/*}'
],
fileFilter: filter(file => CONF[fileExt.toUpperCase()][compression.toUpperCase()])
};
};
return gulp.src(srcModules, {base: 'public', since: gulp.lastRun('task:gzipAssets')})
.pipe(filterGzip)
/**
* build gZip or Brotli assets
* @param config
* @param taskName
* @returns {JQuery.PromiseBase<never, never, never, never, never, never, never, never, never, never, never, never>}
*/
let gzipAssets = (config, taskName) => {
return gulp.src(config.srcModules, {base: 'public', since: gulp.lastRun(taskName)})
.pipe(config.fileFilter)
.pipe(debug({title: 'Gzip asses dest: ', showFiles: false}))
.pipe(bytediff.start())
.pipe(gzip(gZipOptions))
@@ -581,18 +601,17 @@ gulp.task('task:gzipAssets', () => {
}
}))
.pipe(gulp.dest(PATH.ASSETS.DIST));
});
};
gulp.task('task:brotliAssets', () => {
let filterBrotli = filter(file => CONF.BROTLI);
let fileExt = ['js', 'css'];
let srcModules = [
PATH.ASSETS.DIST +'/**/*.{' + fileExt.join(',') + '}',
'!' + PATH.ASSETS.DIST + '/js/' + CONF.TAG + '{,/**/*}'
];
return gulp.src(srcModules, {base: 'public', since: gulp.lastRun('task:brotliAssets')})
.pipe(filterBrotli)
/**
* build Brotli or Brotli assets
* @param config
* @param taskName
* @returns {JQuery.PromiseBase<never, never, never, never, never, never, never, never, never, never, never, never>}
*/
let brotliAssets = (config, taskName) => {
return gulp.src(config.srcModules, {base: 'public', since: gulp.lastRun(taskName)})
.pipe(config.fileFilter)
.pipe(debug({title: 'Brotli asses dest: ', showFiles: false}))
.pipe(bytediff.start())
.pipe(brotli.compress(brotliOptions))
@@ -605,6 +624,22 @@ gulp.task('task:brotliAssets', () => {
}
}))
.pipe(gulp.dest(PATH.ASSETS.DIST));
};
gulp.task('task:gzipJsAssets', () => {
return gzipAssets(getAssetFilterOptions('gzip', 'js'), 'task:gzipJsAssets');
});
gulp.task('task:gzipCssAssets', () => {
return gzipAssets(getAssetFilterOptions('gzip', 'css'), 'task:gzipCssAssets');
});
gulp.task('task:brotliJsAssets', () => {
return brotliAssets(getAssetFilterOptions('brotli', 'js'), 'task:brotliJsAssets');
});
gulp.task('task:brotliCssAssets', () => {
return brotliAssets(getAssetFilterOptions('brotli', 'css'), 'task:brotliCssAssets');
});
/**
@@ -714,13 +749,15 @@ gulp.task('task:configDevelop',
let CONF_DEVELOP = {
JS: {
UGLIFY: false,
SOURCEMAPS: false
SOURCEMAPS: false,
GZIP: false,
BROTLI: false
},
CSS: {
SOURCEMAPS: false
},
GZIP: false,
BROTLI: false
SOURCEMAPS: false,
GZIP: false,
BROTLI: false
}
};
CONF = mergeConf(CONF, CONF_DEVELOP);
@@ -739,13 +776,15 @@ gulp.task('task:configProduction',
let CONF_PRODUCTION = {
JS: {
UGLIFY: true,
SOURCEMAPS: true
SOURCEMAPS: true,
GZIP: true,
BROTLI: true
},
CSS: {
SOURCEMAPS: true
},
GZIP: true,
BROTLI: true
SOURCEMAPS: true,
GZIP: true,
BROTLI: true
}
};
CONF = mergeConf(CONF, CONF_PRODUCTION);
@@ -760,8 +799,8 @@ gulp.task('task:configProduction',
* updates JS destination move to (final) dir
*/
gulp.task('task:updateJsDest', gulp.series(
'task:gzipAssets',
'task:brotliAssets',
'task:gzipJsAssets',
'task:brotliJsAssets',
'task:renameJsDest',
'task:printJsSummary',
'task:cleanJsBuild'
@@ -809,8 +848,8 @@ gulp.task(
gulp.series(
'task:buildCss',
// 'task:cleanCss',
'task:gzipAssets',
'task:brotliAssets',
'task:gzipCssAssets',
'task:brotliCssAssets',
'task:printJsSummary'
)
);

View File

@@ -1,3 +1,5 @@
'use strict';
// main script path
var mainScriptPath = document.body.getAttribute('data-script');
@@ -40,7 +42,7 @@ requirejs.config({
raphael: 'lib/raphael-min', // v2.1.2 Raphaël - required for morris (dependency)
bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.2.0 Inline Chart - http://benpickles.github.io/peity/
peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/
dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select
hoverIntent: 'lib/jquery.hoverIntent.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
fullScreen: 'lib/jquery.fullscreen.min', // v0.6.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
@@ -51,8 +53,9 @@ requirejs.config({
blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery
blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.4.2 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery
bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.5 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation
bootstrapToggle: 'lib/bootstrap2-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com
bootstrapToggle: 'lib/bootstrap-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com
lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload
sortable: 'lib/sortable.min', // v1.6.0 Sortable - drag&drop reorder - https://github.com/rubaxa/Sortable
// header animation
easePack: 'lib/EasePack.min',
@@ -126,7 +129,10 @@ requirejs.config({
},
morris: {
deps: ['jquery', 'raphael'],
exports: 'Morris'
exports: 'Morris',
init: function ($, Raphael) {
window.Raphael = Raphael;
}
},
pnotify: {
deps : ['jquery']

View File

@@ -14,49 +14,77 @@ define([
* update element with time information
* @param element
* @param tempDate
* @param round
*/
let updateDateDiff = function(element, tempDate){
let updateDateDiff = function(element, tempDate, round){
let diff = Util.getTimeDiffParts(tempDate, new Date());
let days = diff.days;
let hrs = diff.hours;
let min = diff.min;
let leftSec = diff.sec;
let value = [];
let parts = [];
if(
days > 0 ||
value.length > 0
round === 'd' &&
days >= 1
){
value.push('<span class="' + config.counterDigitLargeClass + '">' + days + 'd' + '</span>');
}
if(
hrs > 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + hrs + 'h' + '</span>');
}
if(
min > 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + min + 'm' + '</span>');
parts.push('<span class="' + config.counterDigitLargeClass + '">' + '&gt;&nbsp;1d' + '</span>');
}else{
if(
days > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitLargeClass + '">' + days + 'd' + '</span>');
}
if(
hrs > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + hrs + 'h' + '</span>');
}
if(
min > 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + min + 'm' + '</span>');
}
if(
leftSec >= 0 ||
parts.length > 0
){
parts.push('<span class="' + config.counterDigitSmallClass + '">' + leftSec + 's' + '</span>');
}
}
if(
leftSec >= 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + leftSec + 's' + '</span>');
}
element.html(value.join(' '));
element.html(parts.join(' '));
};
/**
* destroy all active counter recursive
*/
$.fn.destroyTimestampCounter = function(){
return this.each(function(){
let element = $(this);
element.find('[data-counter="init"]').each(function(){
let interval = $(this).data('interval');
if(interval){
clearInterval(interval);
element.removeAttr('data-counter')
.removeData('interval')
.removeClass('stopCounter');
}
});
});
};
/**
* init a live counter based on a unix timestamp
* @returns {*}
* @param round string e.g. 'd' => round days
*/
$.fn.initTimestampCounter = function(){
$.fn.initTimestampCounter = function(round){
return this.each(function(){
let element = $(this);
let timestamp = parseInt( element.text() );
@@ -68,7 +96,7 @@ define([
let date = new Date( timestamp * 1000);
updateDateDiff(element, date);
updateDateDiff(element, date, round);
// show element (if invisible) after first update
element.css({'visibility': 'initial'});
@@ -77,7 +105,7 @@ define([
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date);
updateDateDiff(element, date, round);
}else{
clearInterval( element.data('interval') );
}

View File

@@ -327,6 +327,9 @@ define(['jquery'], function($) {
location: 0.6
}]
]
},
active: {
cssClass: 'pf-map-connection-active'
}
},
// signature groups

View File

@@ -205,7 +205,6 @@ define([
class: config.dialogDynamicAreaClass
}).append( graphElement );
// headline
let headline = $('<h4>', {
text: key
}).prepend(

View File

@@ -76,42 +76,6 @@ define([
defaultAcceptCookieExpire: 365 // default expire for "accept coolies" cookie
};
/**
* set a cookie
* @param cname
* @param cvalue
* @param exdays
*/
let setCookie = function(cname, cvalue, exdays) {
let d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
let expires = 'expires=' + d.toUTCString();
let path = 'path=' + Util.getDocumentPath();
document.cookie = cname + '=' + cvalue + '; ' + expires + '; ' + path;
};
/**
* get cookie value by name
* @param cname
* @returns {*}
*/
let getCookie = function(cname) {
let name = cname + '=';
let ca = document.cookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length,c.length);
}
}
return '';
};
/**
* set link observer for "version info" dialog
*/
@@ -146,7 +110,7 @@ define([
moveAdminPanel('down');
});
if(getCookie('cookie') !== '1'){
if(Util.getCookie('cookie') !== '1'){
// hint not excepted
cookieHintElement.collapse('show');
@@ -163,7 +127,7 @@ define([
btnCancelIcon: 'fa fa-fw fa-check',
onCancel: function(e, target){
// "Accept cookies"
setCookie('cookie', 1, config.defaultAcceptCookieExpire);
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
// set "default" href
let href = $(target).data('bs.confirmation').getHref();
@@ -181,7 +145,7 @@ define([
}
cookieHintElement.find('.btn-success').on('click', function(){
setCookie('cookie', 1, config.defaultAcceptCookieExpire);
Util.setCookie('cookie', 1, config.defaultAcceptCookieExpire, 'd');
});
// manual -------------------------------------------------------------

View File

@@ -23,6 +23,7 @@ define([
let config = {
zIndexCounter: 110,
maxActiveConnections: 8,
mapSnapToGrid: false, // "Snap to Grid" feature for drag&drop systems on map (optional)
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
@@ -30,9 +31,9 @@ define([
mapClass: 'pf-map', // class for all maps
mapIdPrefix: 'pf-map-', // id prefix for all maps
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system in a map
systemSelectedClass: 'pf-system-selected', // class for selected systems in a map
systemLockedClass: 'pf-system-locked', // class for locked systems in a map
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on a map
systemLockedClass: 'pf-system-locked', // class for locked systems on a map
systemHeadClass: 'pf-system-head', // class for system head
systemHeadNameClass: 'pf-system-head-name', // class for system name
systemHeadExpandClass: 'pf-system-head-expand', // class for system head expand arrow
@@ -85,7 +86,7 @@ define([
dragOptions:{
},
connectionsDetachable: true, // dragOptions are set -> allow detaching them
maxConnections: 10, // due to isTarget is true, this is the max count of !out!-going connections
maxConnections: 10, // due to isTarget is true, this is the max count of !out!-going connections
// isSource:true,
anchor: 'Continuous'
},
@@ -93,7 +94,7 @@ define([
filter: '.' + config.systemHeadNameClass,
isSource:true,
//isTarget:true,
//allowLoopback: false, // loopback connections are not allowed
//allowLoopBack: false, // loopBack connections are not allowed
cssClass: config.endpointTargetClass,
dropOptions: {
hoverClass: config.systemActiveClass,
@@ -580,7 +581,8 @@ define([
if(alias !== data.alias){
// alias changed
system.find('.' + config.systemHeadNameClass).editable('setValue', data.alias);
alias = data.alias ? data.alias : data.name;
system.find('.' + config.systemHeadNameClass).editable('setValue', alias);
}
}
@@ -626,6 +628,7 @@ define([
// get map container
let mapElement = $( map.getContainer() );
let connectionCanvas = $(connection.canvas);
// if the connection already exists -> do not set it twice
connection.unbind('contextmenu').bind('contextmenu', function(component, e) {
@@ -646,7 +649,7 @@ define([
* init context menu for all connections
* must be triggered manually on demand
*/
$(connection.canvas).contextMenu({
connectionCanvas.contextMenu({
menuSelector: '#' + config.connectionContextMenuId,
menuSelected: function (params){
@@ -708,6 +711,31 @@ define([
}
});
// connection click events ==========================================================================
let single = function(e){
let connection = this;
// left mouse button
if(e.which === 1){
if(e.ctrlKey === true){
// an "active" connection is required before adding more "selected" connections
let activeConnections = MapUtil.getConnectionsByType(map, 'active');
if(activeConnections.length >= config.maxActiveConnections && !connection.hasType('active')){
Util.showNotify({title: 'Connection select limit', text: 'You can´t select more connections', type: 'warning'});
}else {
if(activeConnections.length > 0) {
MapUtil.toggleConnectionActive(map, [connection]);
}else{
MapUtil.showConnectionInfo(map, [connection]);
}
}
}else{
MapUtil.showConnectionInfo(map, [connection]);
}
}
}.bind(connection);
connectionCanvas.singleDoubleClick(single, () => {});
};
/**
@@ -921,7 +949,7 @@ define([
mapWrapper.append(mapContainer);
// append mapWrapper to parent element (at the top)
$(parentElement).prepend(mapWrapper);
parentElement.prepend(mapWrapper);
// set main Container for current map -> the container exists now in DOM !! very important
mapConfig.map.setContainer( config.mapIdPrefix + mapId );
@@ -938,12 +966,17 @@ define([
* @returns {*}
*/
let updateMap = function(mapConfig){
let mapContainer = mapConfig.map.getContainer();
let mapContainer = null;
if(!mapConfig.map){
// jsPlumb needs to be initialized. This is not the case when switching between map tabs right after refresh
return mapContainer;
}else{
mapContainer = $(mapConfig.map.getContainer());
}
let mapId = mapConfig.config.id;
let newSystems = 0;
mapContainer = $(mapContainer);
// add additional information for this map
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
mapContainer.data('name', mapConfig.config.name);
@@ -1650,42 +1683,6 @@ define([
});
};
/**
* get all relevant data for a connection object
* @param connection
* @returns {{id: Number, source: Number, sourceName: (*|T|JQuery|{}), target: Number, targetName: (*|T|JQuery), scope: *, type: *, updated: Number}}
*/
let getDataByConnection = function(connection){
let source = $(connection.source);
let target = $(connection.target);
let id = connection.getParameter('connectionId');
let updated = connection.getParameter('updated');
let connectionTypes = connection.getType();
// normalize connection array
connectionTypes = $.grep(connectionTypes, function(n){
// 'default' is added by jsPlumb by default -_-
return ( n.length > 0 && n !== 'default');
});
let data = {
id: id ? id : 0,
source: parseInt( source.data('id') ),
sourceName: source.data('name'),
sourceAlias: source.getSystemInfo(['alias']) || source.data('name'),
target: parseInt( target.data('id') ),
targetName: target.data('name'),
targetAlias: target.getSystemInfo(['alias']) || target.data('name'),
scope: connection.scope,
type: connectionTypes,
updated: updated ? updated : 0
};
return data;
};
/**
* stores a connection in database
* @param connection
@@ -1697,7 +1694,7 @@ define([
let mapContainer = $( map.getContainer() );
let mapId = mapContainer.data('id');
let connectionData = getDataByConnection(connection);
let connectionData = MapUtil.getDataByConnection(connection);
let requestData = {
mapData: {
@@ -1908,7 +1905,7 @@ define([
]},
{divider: true, action: 'delete_connection'},
{icon: 'fa-trash', action: 'delete_connection', text: 'delete'}
{icon: 'fa-chain-broken', action: 'delete_connection', text: 'detach'}
]
};
@@ -1943,6 +1940,7 @@ define([
{icon: 'fa-plus', action: 'add_system', text: 'add system'},
{icon: 'fa-lock', action: 'lock_system', text: 'lock system'},
{icon: 'fa-volume-up', action: 'set_rally', text: 'set rally point'},
{icon: 'fa-object-group', action: 'select_connections', text: 'select connections'},
{icon: 'fa-tags', text: 'set status', subitems: systemStatus},
{icon: 'fa-reply fa-rotate-180', text: 'waypoints', subitems: [
{subIcon: 'fa-flag-checkered', subAction: 'set_destination', subText: 'set destination'},
@@ -2298,6 +2296,10 @@ define([
currentSystem.markAsChanged();
}
break;
case 'select_connections':
let connections = MapUtil.searchConnectionsBySystems(map, [currentSystem], '*');
MapUtil.showConnectionInfo(map, connections);
break;
case 'change_status_unknown':
case 'change_status_friendly':
case 'change_status_occupied':
@@ -2362,9 +2364,9 @@ define([
if(! system.hasClass('no-click')){
if(e.ctrlKey === true){
// select system
system.toggleSelectSystem(map);
MapUtil.toggleSelectSystem(map, [system]);
}else{
system.showSystemInfo(map);
MapUtil.showSystemInfo(map, system);
}
}
}
@@ -2412,31 +2414,6 @@ define([
return changed;
};
/**
* triggers the "showSystemInfo" event, that is responsible for initializing the "map info" panel
* @param map
*/
$.fn.showSystemInfo = function(map){
let system = $(this);
// activate system
markSystemActive(map, system);
// get parent Tab Content and fire update event
let tabContentElement = MapUtil.getTabContentElementByMapElement( system );
// collect all required data from map module to update the info element
// store them global and assessable for each module
let currentSystemData = {
systemData: system.getSystemData(),
mapId: parseInt( system.attr('data-mapid') )
};
Util.setCurrentSystemData(currentSystemData);
$(tabContentElement).trigger('pf:drawSystemModules');
};
/**
* select all (selectable) systems on a mapElement
*/
@@ -2452,35 +2429,13 @@ define([
return ( $(el).data('locked') !== true );
});
allSystems.toggleSelectSystem(map);
MapUtil.toggleSelectSystem(map, allSystems);
Util.showNotify({title: allSystems.length + ' systems selected', type: 'success'});
});
};
/**
* toggle selectable status of a system
*/
$.fn.toggleSelectSystem = function(map){
return this.each(function(){
let system = $(this);
if( system.data('locked') !== true ){
if( system.hasClass( config.systemSelectedClass ) ){
system.removeClass( config.systemSelectedClass );
map.removeFromDragSelection(system);
}else{
system.addClass( config.systemSelectedClass );
map.addToDragSelection(system);
}
}
});
};
/**
* toggle log status of a system
* @param poke
@@ -2904,7 +2859,7 @@ define([
tempMapWrapper.mCustomScrollbar('scrollTo', system);
// select system
system.showSystemInfo(map);
MapUtil.showSystemInfo(map, system);
}
});
@@ -2969,22 +2924,6 @@ define([
});
};
/**
* mark a system as active
* @param map
* @param system
*/
let markSystemActive = function(map, system){
// deactivate all systems in map
let mapContainer = $( map.getContainer() );
mapContainer.find('.' + config.systemClass).removeClass(config.systemActiveClass);
// set current system active
system.addClass(config.systemActiveClass);
};
/**
* get system data out of its object
* @param info
@@ -3051,7 +2990,7 @@ define([
// data for header update
let headerUpdateData = {
mapId: userData.config.id,
userCount: 0 // active user in a map
userCount: 0 // active user on a map
};
if(
@@ -3191,7 +3130,7 @@ define([
// format connections
for(let j = 0; j < connections.length; j++){
let tempConnection = connections[j];
let connectionData = getDataByConnection(tempConnection);
let connectionData = MapUtil.getDataByConnection(tempConnection);
// only add valid connections (id is required, this is not the case if connection is new)
if(connectionData.id > 0){
@@ -3389,10 +3328,14 @@ define([
let mapElement = mapTabContentElement.find('.' + config.mapClass);
let mapId = mapElement.data('id');
scrollableElement.initCustomScrollbar({
callbacks: {
onScroll: function(){
// scroll complete
// update scroll position for drag-frame-selection
mapElement.attr('data-scroll-left', this.mcs.left);
mapElement.attr('data-scroll-top', this.mcs.top);
// store new map scrollOffset -> localDB
MapUtil.storeLocalData('map', mapId, 'offsetX', Math.abs(this.mcs.left) );
},
@@ -3402,11 +3345,6 @@ define([
// hide all system head tooltips
$(this).find('.' + config.systemHeadClass + ' .fa').tooltip('hide');
},
whileScrolling:function(){
// update scroll position for drag-frame-selection
mapElement.data('scrollLeft', this.mcs.left);
mapElement.data('scrollTop', this.mcs.top);
}
}
});
@@ -3422,7 +3360,6 @@ define([
return {
getMapInstance: getMapInstance,
clearMapInstance: clearMapInstance,
getDataByConnection: getDataByConnection,
showNewSystemDialog: showNewSystemDialog
};

View File

@@ -5,8 +5,9 @@
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'app/util',
'app/map/util'
], function($, Init, Util, MapUtil) {
'use strict';
let config = {
@@ -72,34 +73,15 @@ define([
* @param label
*/
let addEndpointOverlay = (endpoint, label) => {
let newLabel = '';
let colorClass = 'txt-color-grayLighter';
if(label.length > 0){
newLabel = label;
// check if multiple labels found => conflict
if( label.includes(', ') ){
colorClass = 'txt-color-orangeLight';
}else if( !label.includes('K162') ){
colorClass = 'txt-color-yellow';
}
}else{
// endpoint not connected with a signature
newLabel = '<i class="fa fa-question-circle"></i>';
colorClass = 'txt-color-red';
}
endpoint.addOverlay([
'Label',
{
label: '<span class="txt-color ' + colorClass + '">' + newLabel + '</span>',
label: MapUtil.getEndpointOverlayContent(label),
id: config.connectionOverlaySmallId,
cssClass: config.connectionOverlaySmallClass,
location: [ 0.5, 0.5 ]
location: [ 0.9, 0.9 ]
}
]);
};
// loop through all map connections (get from DOM)
@@ -119,53 +101,15 @@ define([
// ... find matching connectionData (from Ajax)
for(let connectionData of connectionsData){
if(
connectionData.id === connectionId &&
connectionData.signatures // signature data is required...
){
// ... collect overlay/label data from signatures
for(let signatureData of connectionData.signatures){
// ... typeId is required to get a valid name
if(signatureData.typeId > 0){
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'sourceLabels';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'targetLabels';
tmpSystem = targetSystem;
}
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatures.getAllSignatureNamesBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if( flattenSigTypeNames.hasOwnProperty(signatureData.typeId) ){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the in game name
label = label.substr(0, label.indexOf(' '));
signatureTypeNames[tmpSystemType].push(label);
}
}
}
}
if(connectionData.id === connectionId){
signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
// ... connection matched -> continue with next one
break;
}
}
let sourceLabel = signatureTypeNames.sourceLabels.join(', ');
let targetLabel = signatureTypeNames.targetLabels.join(', ');
let sourceLabel = signatureTypeNames.sourceLabels;
let targetLabel = signatureTypeNames.targetLabels;
// add endpoint overlays ------------------------------------------------------
addEndpointOverlay(sourceEndpoint, sourceLabel);
@@ -176,19 +120,19 @@ define([
let arrowDirection = 1;
if(
(sourceLabel.includes('K162') && targetLabel.includes('K162')) ||
(sourceLabel.indexOf('K162') !== -1 && targetLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.length === 0) ||
(
sourceLabel.length > 0 && targetLabel.length > 0 &&
!sourceLabel.includes('K162') && !targetLabel.includes('K162')
sourceLabel.indexOf('K162') === -1 && targetLabel.indexOf('K162') === -1
)
){
// unknown direction
overlayType = 'Diamond'; // not specified
arrowDirection = 1;
}else if(
(sourceLabel.includes('K162')) ||
(sourceLabel.length === 0 && !targetLabel.includes('K162'))
(sourceLabel.indexOf('K162') !== -1) ||
(sourceLabel.length === 0 && targetLabel.indexOf('K162') === -1)
){
// convert default arrow direction
overlayType = 'Arrow';
@@ -283,7 +227,9 @@ define([
showLoading(overlayConnectionIcon);
let requestData = {
mapId: mapElement.data('id')
mapId: mapElement.data('id'),
addData : ['signatures'],
filterData : ['signatures']
};
$.ajax({

View File

@@ -18,7 +18,7 @@ define([
y: 0
},
systemActiveClass: 'pf-system-active', // class for an active system in a map
systemActiveClass: 'pf-system-active', // class for an active system on a map
dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog

View File

@@ -24,7 +24,8 @@ define([
systemIdPrefix: 'pf-system-', // id prefix for a system
systemClass: 'pf-system', // class for all systems
systemSelectedClass: 'pf-system-selected', // class for selected systems in a map
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on on map
// dataTable
tableCellEllipsisClass: 'pf-table-cell-ellipsis',
@@ -60,7 +61,7 @@ define([
* @param {bool} filterByUser
* @returns {Array}
*/
let getMapTypes = function(filterByUser){
let getMapTypes = (filterByUser) => {
let mapTypes = [];
$.each(Init.mapTypes, function(prop, data){
@@ -109,7 +110,7 @@ define([
* get all available scopes for a map
* @returns {Array}
*/
let getMapScopes = function(){
let getMapScopes = () => {
let scopes = [];
$.each(Init.mapScopes, function(prop, data){
let tempData = data;
@@ -126,7 +127,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getScopeInfoForMap = function(info, option){
let getScopeInfoForMap = (info, option) => {
let scopeInfo = '';
if(Init.mapScopes.hasOwnProperty(info)){
scopeInfo = Init.mapScopes[info][option];
@@ -138,7 +139,7 @@ define([
* get all available map icons
* @returns {Object[]}
*/
let getMapIcons = function(){
let getMapIcons = () => {
return Init.mapIcons;
};
@@ -148,7 +149,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getInfoForMap = function(mapType, option){
let getInfoForMap = (mapType, option) => {
let mapInfo = '';
if(Init.mapTypes.hasOwnProperty(mapType)){
mapInfo = Init.mapTypes[mapType][option];
@@ -162,7 +163,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getInfoForSystem = function(info, option){
let getInfoForSystem = (info, option) => {
let systemInfo = '';
if(Init.classes.systemInfo.hasOwnProperty(info)){
systemInfo = Init.classes.systemInfo[info][option];
@@ -176,7 +177,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getSystemTypeInfo = function(systemTypeId, option){
let getSystemTypeInfo = (systemTypeId, option) => {
let systemTypeInfo = '';
$.each(Init.systemType, function(prop, data){
if(systemTypeId === data.id){
@@ -193,7 +194,7 @@ define([
* @param option
* @returns {string}
*/
let getEffectInfoForSystem = function(effect, option){
let getEffectInfoForSystem = (effect, option) => {
let effectInfo = '';
if( Init.classes.systemEffects.hasOwnProperty(effect) ){
effectInfo = Init.classes.systemEffects[effect][option];
@@ -210,7 +211,7 @@ define([
};
/**
* get all selected (NOT active) systems in a map
* get all selected (NOT active) systems on a map
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
@@ -219,21 +220,327 @@ define([
};
/**
* search connections by systems
* @param {Object} map - jsPlumb
* @param {JQuery[]} systems - system DOM elements
* @returns {Array} connections - found connection, DOM elements
* filter connections by type
* @param map
* @param type
* @returns {Array}
*/
let searchConnectionsBySystems = function(map, systems){
let getConnectionsByType = (map, type) => {
let connections = [];
// iterate through ALL connections and filter...
// -> there is no "filterByScope()" method in jsPlumb
for(let connection of map.getAllConnections()){
if(connection.getType().indexOf(type) !== -1){
connections.push(connection);
}
}
return connections;
};
/**
* get all relevant data for a connection object
* @param connection
* @returns {{id: Number, source: Number, sourceName: (*|T|JQuery|{}), target: Number, targetName: (*|T|JQuery), scope: *, type: *, updated: Number}}
*/
let getDataByConnection = (connection) => {
let source = $(connection.source);
let target = $(connection.target);
let id = connection.getParameter('connectionId');
let updated = connection.getParameter('updated');
let connectionTypes = connection.getType();
// normalize connection array
connectionTypes = $.grep(connectionTypes, function(n){
// 'default' is added by jsPlumb by default -_-
return ( n.length > 0 && n !== 'default' && n !== 'active');
});
let data = {
id: id ? id : 0,
source: parseInt( source.data('id') ),
sourceName: source.data('name'),
sourceAlias: source.getSystemInfo(['alias']) || source.data('name'),
target: parseInt( target.data('id') ),
targetName: target.data('name'),
targetAlias: target.getSystemInfo(['alias']) || target.data('name'),
scope: connection.scope,
type: connectionTypes,
updated: updated ? updated : 0
};
return data;
};
/**
* @see getDataByConnection
* @param connections
* @returns {Array}
*/
let getDataByConnections = (connections) => {
let data = [];
for(let connection of connections){
data.push(getDataByConnection(connection));
}
return data;
};
/**
* get connection related data from a connection
* -> data requires a signature bind to that connection
* @param connection
* @param connectionData
* @returns {{sourceLabels: Array, targetLabels: Array}}
*/
let getConnectionDataFromSignatures = (connection, connectionData) => {
let signatureTypeNames = {
sourceLabels: [],
targetLabels: []
};
if(
connection &&
connectionData.signatures // signature data is required...
){
let SystemSignatures = require('app/ui/system_signature');
let connectionId = connection.getParameter('connectionId');
let sourceEndpoint = connection.endpoints[0];
let targetEndpoint = connection.endpoints[1];
let sourceSystem = $(sourceEndpoint.element);
let targetSystem = $(targetEndpoint.element);
let sourceId = sourceSystem.data('id');
let targetId = targetSystem.data('id');
// ... collect overlay/label data from signatures
for(let signatureData of connectionData.signatures){
// ... typeId is required to get a valid name
if(signatureData.typeId > 0){
// whether "source" or "target" system is relevant for current connection and current signature...
let tmpSystem = null;
let tmpSystemType = null;
if(signatureData.system.id === sourceId){
// relates to "source" endpoint
tmpSystemType = 'sourceLabels';
tmpSystem = sourceSystem;
}else if(signatureData.system.id === targetId){
// relates to "target" endpoint
tmpSystemType = 'targetLabels';
tmpSystem = targetSystem;
}
// ... get endpoint label for source || target system
if(tmpSystem && tmpSystem){
// ... get all available signature type (wormholes) names
let availableSigTypeNames = SystemSignatures.getAllSignatureNamesBySystem(tmpSystem, 5);
let flattenSigTypeNames = Util.flattenXEditableSelectArray(availableSigTypeNames);
if( flattenSigTypeNames.hasOwnProperty(signatureData.typeId) ){
let label = flattenSigTypeNames[signatureData.typeId];
// shorten label, just take the in game name
label = label.substr(0, label.indexOf(' '));
signatureTypeNames[tmpSystemType].push(label);
}
}
}
}
}
return signatureTypeNames;
};
/**
* get overlay HTML for connection endpoints by Label array
* @param label
* @returns {string}
*/
let getEndpointOverlayContent = (label) => {
let newLabel = '';
let colorClass = 'txt-color-grayLighter';
if(label.length > 0){
newLabel = label.join(', ');
// check if multiple labels found => conflict
if( newLabel.includes(', ') ){
colorClass = 'txt-color-orangeLight';
}else if( !newLabel.includes('K162') ){
colorClass = 'txt-color-yellow';
}
}else{
// endpoint not connected with a signature
newLabel = '<i class="fa fa-question-circle"></i>';
colorClass = 'txt-color-red';
}
return '<span class="txt-color ' + colorClass + '">' + newLabel + '</span>';
};
/**
* get TabContentElement by any element on a map e.g. system
* @param element
* @returns {*}
*/
let getTabContentElementByMapElement = (element) => {
let tabContentElement = $(element).parents('.' + config.mapTabContentClass);
return tabContentElement;
};
/**
* checks if there is an "active" connection on a map
* @param map
* @returns {boolean}
*/
let hasActiveConnection = (map) => {
let activeConnections = getConnectionsByType(map, 'active');
return activeConnections.length > 0;
};
/**
* mark a system as "active"
* @param map
* @param system
*/
let setSystemActive = (map, system) => {
// deselect all selected systems on map
let mapContainer = $( map.getContainer() );
mapContainer.find('.' + config.systemClass).removeClass(config.systemActiveClass);
// set current system active
system.addClass(config.systemActiveClass);
};
/**
* mark a connection as "active"
* @param connections
*/
let setConnectionsActive = (map, connections) => {
// set all inactive
for(let connection of getConnectionsByType(map, 'active')){
connection.removeType('active');
}
for(let connection of connections){
connection.addType('active');
}
};
/**
* toggle "selected" status of system
* @param map
* @param systems
*/
let toggleSelectSystem = (map, systems) => {
for(let system of systems){
system = $(system);
if( system.data('locked') !== true ){
if( system.hasClass( config.systemSelectedClass ) ){
system.removeClass( config.systemSelectedClass );
map.removeFromDragSelection(system);
}else{
system.addClass( config.systemSelectedClass );
map.addToDragSelection(system);
}
}
}
};
/**
* toggle "selected" status of connections
* @param map
* @param connections
*/
let toggleConnectionActive = (map, connections) => {
let selectedConnections = [];
let deselectedConnections = [];
for(let connection of connections){
if(connection.hasType('active')){
connection.removeType('active');
deselectedConnections.push(connection);
}else{
connection.addType('active');
selectedConnections.push(connection);
}
}
updateConnectionInfo(map, selectedConnections, deselectedConnections);
};
/**
* show system info panels
* @param map
* @param system
*/
let showSystemInfo = (map, system) => {
setSystemActive(map, system);
// get parent Tab Content and fire update event
let tabContentElement = getTabContentElementByMapElement( system );
// collect all required data from map module to update the info element
// store them global and assessable for each module
Util.setCurrentSystemData({
systemData: system.getSystemData(),
mapId: parseInt( system.attr('data-mapid') )
});
$(tabContentElement).trigger('pf:drawSystemModules');
};
/**
* show connection info panels
* @param map
* @param connections
*/
let showConnectionInfo = (map, connections) => {
setConnectionsActive(map, connections);
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
let tabContentElement = getTabContentElementByMapElement(mapContainer);
$(tabContentElement).trigger('pf:drawConnectionModules', {
connections: connections,
mapId: parseInt(mapContainer.data('id'))
});
};
/**
* update connection info panels
* @param map
* @param selectedConnections
* @param deselectedConnections
*/
let updateConnectionInfo = (map, selectedConnections, deselectedConnections) => {
// get parent Tab Content and fire update event
let mapContainer = $(map.getContainer());
$(document).trigger('pf:updateConnectionInfoModule', {
connectionsUpdate: selectedConnections,
connectionsRemove: deselectedConnections,
mapId: parseInt(mapContainer.data('id'))
});
};
/**
* search connections by systems
* @param map
* @param systems
* @param scope
* @returns {Array}
*/
let searchConnectionsBySystems = (map, systems, scope) => {
let connections = [];
let withBackConnection = true;
$.each(systems, function(i, system){
// get connections where system is source
connections = connections.concat( map.getConnections({source: system}) );
connections = connections.concat( map.getConnections({scope: scope, source: system}) );
if(withBackConnection === true){
// get connections where system is target
connections = connections.concat( map.getConnections({target: system}) );
connections = connections.concat( map.getConnections({scope: scope, target: system}) );
}
});
return connections;
@@ -247,7 +554,7 @@ define([
* @param {string|string[]} type
* @returns {Array}
*/
let searchConnectionsByScopeAndType = function(map, scope, type){
let searchConnectionsByScopeAndType = (map, scope, type) => {
let connections = [];
let scopeArray = (scope === undefined) ? ['*'] : ((Array.isArray(scope)) ? scope : [scope]);
let typeArray = (type === undefined) ? [] : ((Array.isArray(type)) ? type : [type]);
@@ -276,7 +583,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getConnectionInfo = function(connectionTyp, option){
let getConnectionInfo = (connectionTyp, option) => {
let connectionInfo = '';
if(Init.connectionTypes.hasOwnProperty(connectionTyp)){
connectionInfo = Init.connectionTypes[connectionTyp][option];
@@ -291,7 +598,7 @@ define([
* @param {JQuery} systemB
* @returns {Array}
*/
let checkForConnection = function(map, systemA, systemB){
let checkForConnection = (map, systemA, systemB) => {
let connections = [];
connections = connections.concat( map.getConnections({scope: '*', source: systemA, target: systemB}) );
// get connections where system is target
@@ -305,7 +612,7 @@ define([
* @param {string} scope
* @returns {string}
*/
let getDefaultConnectionTypeByScope = function(scope){
let getDefaultConnectionTypeByScope = (scope) => {
let type = '';
switch(scope){
case 'wh':
@@ -329,7 +636,7 @@ define([
* @param {Object} connection - jsPlumb object
* @param {string} status
*/
let setConnectionWHStatus = function(connection, status){
let setConnectionWHStatus = (connection, status) => {
if(
status === 'wh_fresh' &&
connection.hasType('wh_fresh') !== true
@@ -370,7 +677,7 @@ define([
* @param {string} option
* @returns {string}
*/
let getScopeInfoForConnection = function(info, option){
let getScopeInfoForConnection = (info, option) => {
let scopeInfo = '';
if(Init.connectionScopes.hasOwnProperty(info)){
switch(option){
@@ -388,21 +695,11 @@ define([
return scopeInfo;
};
/**
* get TabContentElement by any element on a map e.g. system
* @param element
* @returns {*}
*/
let getTabContentElementByMapElement = function(element){
let tabContentElement = $(element).parents('.' + config.mapTabContentClass);
return tabContentElement;
};
/**
* store mapId for current user (IndexedDB)
* @param mapId
*/
let storeDefaultMapId = function(mapId){
let storeDefaultMapId = (mapId) => {
if(mapId > 0){
let userData = Util.getCurrentUserData();
if(
@@ -419,7 +716,7 @@ define([
* @param type
* @returns {boolean}
*/
let getLocalStoragePrefixByType = function(type){
let getLocalStoragePrefixByType = (type) => {
let prefix = false;
switch(type){
case 'character': prefix = config.characterLocalStoragePrefix; break;
@@ -435,7 +732,7 @@ define([
* @param objectId
* @returns {*}
*/
let getLocaleData = function(type, objectId){
let getLocaleData = (type, objectId) => {
if(objectId > 0){
let storageKey = getLocalStoragePrefixByType(type) + objectId;
return Util.getLocalStorage().getItem(storageKey);
@@ -451,7 +748,7 @@ define([
* @param key
* @param value
*/
let storeLocalData = function(type, objectId, key, value){
let storeLocalData = (type, objectId, key, value) => {
if(objectId > 0){
// get current map config
let storageKey = getLocalStoragePrefixByType(type) + objectId;
@@ -481,7 +778,7 @@ define([
* @param objectId
* @param key
*/
let deleteLocalData = function(type, objectId, key){
let deleteLocalData = (type, objectId, key) => {
if(objectId > 0){
// get current map config
let storageKey = getLocalStoragePrefixByType(type) + objectId;
@@ -625,6 +922,56 @@ define([
});
};
/**
* add a wormhole tooltip with wh specific data to elements
* @param tooltipData
* @returns {*}
*/
$.fn.addWormholeInfoTooltip = function(tooltipData){
return this.each(function() {
let element = $(this);
requirejs(['text!templates/tooltip/wormhole_info.html', 'mustache'], function (template, Mustache) {
// format tooltip data
let data = {};
if(tooltipData.massTotal){
data.massTotal = Util.formatMassValue(tooltipData.massTotal);
}
if(tooltipData.massIndividual){
data.massIndividual = Util.formatMassValue(tooltipData.massIndividual);
}
if(tooltipData.massRegeneration){
data.massRegeneration = Util.formatMassValue(tooltipData.massRegeneration);
}
if(tooltipData.maxStableTime){
data.maxStableTime = tooltipData.maxStableTime + ' h';
}
let title = tooltipData.name +
'<span class="pull-right ' + tooltipData.class +'">' + tooltipData.security + '</span>';
let content = Mustache.render(template, data);
element.popover({
placement: 'top',
html: true,
trigger: 'hover',
content: '',
container: 'body',
title: title,
delay: {
show: 150,
hide: 0
}
});
// set new popover content
let popover = element.data('bs.popover');
popover.options.title = title;
popover.options.content = content;
});
});
};
$.fn.findMapElement = function(){
return $(this).find('.' + config.mapClass);
};
@@ -650,6 +997,12 @@ define([
getInfoForSystem: getInfoForSystem,
getSystemTypeInfo: getSystemTypeInfo,
getEffectInfoForSystem: getEffectInfoForSystem,
toggleSelectSystem: toggleSelectSystem,
toggleConnectionActive: toggleConnectionActive,
showSystemInfo: showSystemInfo,
showConnectionInfo: showConnectionInfo,
getConnectionsByType: getConnectionsByType,
getDataByConnection: getDataByConnection,
searchConnectionsBySystems: searchConnectionsBySystems,
searchConnectionsByScopeAndType: searchConnectionsByScopeAndType,
getConnectionInfo: getConnectionInfo,
@@ -657,7 +1010,11 @@ define([
getDefaultConnectionTypeByScope: getDefaultConnectionTypeByScope,
setConnectionWHStatus: setConnectionWHStatus,
getScopeInfoForConnection: getScopeInfoForConnection,
getDataByConnections: getDataByConnections,
getConnectionDataFromSignatures: getConnectionDataFromSignatures,
getEndpointOverlayContent: getEndpointOverlayContent,
getTabContentElementByMapElement: getTabContentElementByMapElement,
hasActiveConnection: hasActiveConnection,
storeDefaultMapId: storeDefaultMapId,
getLocaleData: getLocaleData,
storeLocalData: storeLocalData,

View File

@@ -64,6 +64,7 @@ define([
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
Init.wormholes = initData.wormholes;
Init.characterStatus = initData.characterStatus;
Init.routes = initData.routes;
Init.url = initData.url;
@@ -382,7 +383,18 @@ define([
// Send map update request on tab close/reload, in order to save map changes that
// haven´t been saved through default update trigger
window.addEventListener('beforeunload', function(e) {
// save unsaved map changes ...
triggerMapUpdatePing();
// check if character should be switched on reload or current character should be loaded afterwards
let characterSwitch = Boolean( $('body').data('characterSwitch') );
if(!characterSwitch){
let characterId = Util.getCurrentCharacterId();
if(characterId){
Util.setCookie('old_char_id', characterId, 3, 's');
}
}
// IMPORTANT, return false in order to not "abort" ajax request in background!
return false;
});

View File

@@ -4,14 +4,28 @@ define([
'app/util',
'app/map/map',
'app/map/util',
'app/counter',
'sortable',
'app/ui/system_info',
'app/ui/system_graph',
'app/ui/system_signature',
'app/ui/system_route',
'app/ui/system_killboard'
], function($, Init, Util, Map, MapUtil) {
'app/ui/system_killboard',
'app/ui/connection_info',
'app/counter'
], function(
$,
Init,
Util,
Map,
MapUtil,
Sortable,
SystemInfoModule,
SystemGraphModule,
SystemSignatureModule,
SystemRouteModule,
SystemKillboardModule,
ConnectionInfoModule
){
'use strict';
let config = {
@@ -67,118 +81,245 @@ define([
*/
$.fn.setTabContentObserver = function(){
return this.each(function(){
let tabContentElement = $(this);
// update Tab Content with system data information
$(this).on('pf:drawSystemModules', function(e){
drawSystemModules($( e.target ));
tabContentElement.on('pf:drawSystemModules', function(e){
drawSystemModules($(e.target));
});
$(this).on('pf:removeSystemModules', function(e){
removeSystemModules($( e.target ));
tabContentElement.on('pf:removeSystemModules', function(e){
removeSystemModules($(e.target));
});
tabContentElement.on('pf:drawConnectionModules', function(e, data){
drawConnectionModules($(e.target), data);
});
tabContentElement.on('pf:removeConnectionModules', function(e){
removeConnectionModules($(e.target));
});
});
};
/**
* clear all system info modules and remove them
* remove multiple modules
* @param tabContentElement
* @param modules
*/
let removeModules = (tabContentElement, modules) => {
for(let Module of modules){
let moduleElement = tabContentElement.find('.' + Module.config.moduleTypeClass);
removeModule(moduleElement, Module);
}
};
/**
* clear all system modules and remove them
* @param tabContentElement
*/
let removeSystemModules = (tabContentElement) => {
let systemModules = [SystemInfoModule, SystemGraphModule, SystemSignatureModule, SystemRouteModule, SystemKillboardModule];
removeModules(tabContentElement, systemModules);
};
/**
* clear all connection modules and remove them
* @param tabContentElement
*/
let removeConnectionModules = (tabContentElement) => {
let connectionModules = [ConnectionInfoModule];
removeModules(tabContentElement, connectionModules);
};
/**
* remove a single module
* @param moduleElement
* @param Module
* @param callback
*/
let removeSystemModules = function(tabContentElement, callback){
tabContentElement.find('.' + config.moduleClass).velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
if(callback){
callback();
}
let removeModule = (moduleElement, Module, callback) => {
if(moduleElement.length > 0){
if(typeof Module.beforeReDraw === 'function'){
Module.beforeReDraw();
}
});
moduleElement.velocity('reverse',{
complete: function(moduleElement){
moduleElement = $(moduleElement);
if(typeof Module.beforeDestroy === 'function'){
Module.beforeDestroy(moduleElement);
}
moduleElement.remove();
if(typeof callback === 'function'){
callback();
}
}
});
}
};
/**
* generic function that draws a modulePanel for a given Module object
* @param parentElement
* @param Module
* @param mapId
* @param data
*/
let drawModule = (parentElement, Module, mapId, data) => {
/**
* get module position within its parentElement
* @param parentElement
* @param Module
* @param defaultPosition
* @returns {number}
*/
let getModulePosition = (parentElement, Module, defaultPosition) => {
let position = 0;
if(defaultPosition > 0){
parentElement.children().each((i, moduleElement) => {
position = i + 1;
let tempPosition = parseInt(moduleElement.getAttribute('data-position')) || 0;
if(tempPosition >= defaultPosition){
position--;
return false;
}
});
}
return position;
};
/**
* show/render a Module
* @param parentElement
* @param Module
* @param mapId
* @param data
*/
let showPanel = (parentElement, Module, mapId, data) => {
let moduleElement = Module.getModule(parentElement, mapId, data);
if (moduleElement) {
// store Module object to DOM element for further access
moduleElement.data('module', Module);
moduleElement.data('data', data);
moduleElement.addClass([config.moduleClass, Module.config.moduleTypeClass].join(' '));
moduleElement.css({opacity: 0}); // will be animated
// check module position from local storage
let promiseStore = MapUtil.getLocaleData('map', mapId);
promiseStore.then(function (dataStore) {
let Module = this.moduleElement.data('module');
let defaultPosition = Module.config.modulePosition || 0;
// check for stored module order in indexDB (client) ----------------------------------------------
let key = 'modules_cell_' + this.parentElement.attr('data-position');
if (
dataStore &&
dataStore[key]
) {
let positionIndex = dataStore[key].indexOf(Module.config.moduleName);
if (positionIndex !== -1) {
// first index (0) => is position 1
defaultPosition = positionIndex + 1;
}
}
// find correct position for new moduleElement ----------------------------------------------------
let position = getModulePosition(this.parentElement, Module, defaultPosition);
this.moduleElement.attr('data-position', defaultPosition);
this.moduleElement.attr('data-module', Module.config.moduleName);
// insert at correct position ---------------------------------------------------------------------
let prevModuleElement = this.parentElement.find('.' + config.moduleClass + ':nth-child(' + position + ')');
if (prevModuleElement.length) {
this.moduleElement.insertAfter(prevModuleElement);
} else {
this.parentElement.prepend(this.moduleElement);
}
if(typeof Module.beforeShow === 'function'){
Module.beforeShow(this.moduleElement, moduleElement.data('data'));
}
// show animation ---------------------------------------------------------------------------------
this.moduleElement.velocity({
opacity: [1, 0],
translateY: [0, +20]
}, {
duration: Init.animationSpeed.mapModule,
easing: 'easeOutSine',
complete: function (moduleElement) {
moduleElement = $(moduleElement);
let Module = $(moduleElement).data('module');
if (typeof Module.initModule === 'function') {
Module.initModule(moduleElement, mapId, moduleElement.data('data'));
}
}
});
}.bind({
parentElement: parentElement,
moduleElement: moduleElement
}));
}
};
// check if module already exists
let moduleElement = parentElement.find('.' + Module.config.moduleTypeClass);
if(moduleElement.length > 0){
removeModule(moduleElement, Module, () => {
showPanel(parentElement, Module, mapId, data);
});
}else{
showPanel(parentElement, Module, mapId, data);
}
};
/**
* clears and updates the system info element (signature table, system info,...)
* @param tabContentElement
*/
let drawSystemModules = function(tabContentElement){
require(['datatables.loader'], () => {
let drawSystemModules = (tabContentElement) => {
require(['datatables.loader'], function(){
let currentSystemData = Util.getCurrentSystemData();
// get Table cell for system Info
let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst);
let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond);
// get grid cells
let firstCell = tabContentElement.find('.' + config.mapTabContentCellFirst);
let secondCell = tabContentElement.find('.' + config.mapTabContentCellSecond);
// draw system info module
firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData);
drawModule(firstCell, SystemInfoModule, currentSystemData.mapId, currentSystemData.systemData);
// draw system graph module
firstCell.drawSystemGraphModule(currentSystemData.systemData);
drawModule(firstCell, SystemGraphModule, currentSystemData.mapId, currentSystemData.systemData);
// draw signature table module
firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData);
drawModule(firstCell, SystemSignatureModule, currentSystemData.mapId, currentSystemData.systemData);
// draw system routes module
secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData);
drawModule(secondCell, SystemRouteModule, currentSystemData.mapId, currentSystemData.systemData);
// draw system killboard module
secondCell.drawSystemKillboardModule(currentSystemData.systemData);
// set Module Observer
setModuleObserver();
drawModule(secondCell, SystemKillboardModule, currentSystemData.mapId, currentSystemData.systemData);
});
};
/**
* set observer for each module
* clears and updates the connection info element (mass log)
* @param tabContentElement
* @param data
*/
let setModuleObserver = function(){
let drawConnectionModules = (tabContentElement, data) => {
require(['datatables.loader'], function(){
// toggle height for a module
$(document).off('click.toggleModuleHeight').on('click.toggleModuleHeight', '.' + config.moduleClass, function(e){
let moduleElement = $(this);
// get click position
let posX = moduleElement.offset().left;
let posY = moduleElement.offset().top;
let clickX = e.pageX - posX;
let clickY = e.pageY - posY;
// get grid cells
let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst);
// check for top-left click
if(clickX <= 8 && clickY <= 8){
// remember height
if(! moduleElement.data('origHeight')){
moduleElement.data('origHeight', moduleElement.outerHeight());
}
if(moduleElement.hasClass( config.moduleClosedClass )){
let moduleHeight = moduleElement.data('origHeight');
moduleElement.velocity('finish').velocity({
height: [ moduleHeight + 'px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.removeClass( config.moduleClosedClass );
moduleElement.removeData();
}
});
}else{
moduleElement.velocity('finish').velocity({
height: [ '35px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.addClass( config.moduleClosedClass );
}
});
}
}
});
// draw connection info module
drawModule(firstCell, ConnectionInfoModule, this.mapId, this.connections);
}.bind(data));
};
/**
* updates only visible/active map module
* @returns {boolean}
@@ -236,26 +377,115 @@ define([
}
};
/**
* set observer for tab content (area where modules will be shown)
* @param contentStructure
* @param mapId
*/
let setContentStructureObserver = (contentStructure, mapId) => {
contentStructure.find('.' + config.mapTabContentCell).each((index, cellElement) => {
let sortable = Sortable.create(cellElement, {
group: {
name: 'cell_' + cellElement.getAttribute('data-position')
},
animation: Init.animationSpeed.mapModule,
handle: '.pf-module-handler-drag',
draggable: '.' + config.moduleClass,
ghostClass: 'pf-sortable-ghost',
scroll: true,
scrollSensitivity: 50,
scrollSpeed: 20,
dataIdAttr: 'data-module',
sort: true,
store: {
get: function (sortable) {
return [];
},
set: function (sortable) {
let key = 'modules_' + sortable.options.group.name;
MapUtil.storeLocalData('map', mapId, key, sortable.toArray());
}
},
onStart: function (e) {
// Element dragging started
// -> save initial sort state -> see store.set()
this.save();
}
});
});
// toggle height for a module
contentStructure.on('click.toggleModuleHeight', '.' + config.moduleClass, function(e){
let moduleElement = $(this);
// get click position
let posX = moduleElement.offset().left;
let posY = moduleElement.offset().top;
let clickX = e.pageX - posX;
let clickY = e.pageY - posY;
// check for top-left click
if(clickX <= 8 && clickY <= 8){
// remember height
if(! moduleElement.data('origHeight')){
moduleElement.data('origHeight', moduleElement.outerHeight());
}
if(moduleElement.hasClass( config.moduleClosedClass )){
let moduleHeight = moduleElement.data('origHeight');
moduleElement.velocity('finish').velocity({
height: [ moduleHeight + 'px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.removeClass( config.moduleClosedClass );
moduleElement.removeData('origHeight');
}
});
}else{
moduleElement.velocity('finish').velocity({
height: [ '35px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeOutSine',
complete: function(){
moduleElement.addClass( config.moduleClosedClass );
}
});
}
}
});
};
/**
* load all structure elements into a TabsContent div (tab body)
*/
$.fn.initContentStructure = function(){
return this.each(function(){
// init bootstrap Grid
let contentStructure = $('<div>', {
class: ['row', config.mapTabContentRow].join(' ')
}).append(
let initContentStructure = (tabContentElements) => {
tabContentElements.each(function(){
let tabContentElement = $(this);
let mapId = parseInt( tabContentElement.attr('data-mapid') );
// "add" tab does not need a structure and obervers...
if(mapId > 0){
let contentStructure = $('<div>', {
class: ['row', config.mapTabContentRow].join(' ')
}).append(
$('<div>', {
class: ['col-xs-12', 'col-md-8', config.mapTabContentCellFirst, config.mapTabContentCell].join(' ')
})
}).attr('data-position', 1)
).append(
$('<div>', {
class: ['col-xs-12', 'col-md-4', config.mapTabContentCellSecond, config.mapTabContentCell].join(' ')
})
}).attr('data-position', 2)
);
// append grid structure
$(this).append(contentStructure);
// append grid structure
tabContentElement.append(contentStructure);
setContentStructureObserver(contentStructure, mapId);
}
});
};
@@ -264,7 +494,7 @@ define([
* @param options
* @returns {*|jQuery|HTMLElement}
*/
let getTabElement = function(options){
let getTabElement = (options) => {
let tabElement = $('<div>', {
id: config.mapTabElementId
});
@@ -401,17 +631,17 @@ define([
// update Tab element -> set data
linkElement.updateTabData(options);
// tabs content =======================================================
// tabs content -----------------------------------------------------------------------------------------------
let contentElement = $('<div>', {
id: config.mapTabIdPrefix + parseInt( options.id ),
class: [config.mapTabContentClass].join(' ')
});
}).attr('data-mapid', parseInt( options.id ));
contentElement.addClass('tab-pane');
tabContent.append(contentElement);
// init tab ===========================================================
// init tab ---------------------------------------------------------------------------------------------------
linkElement.on('click', function(e){
e.preventDefault();
@@ -603,7 +833,7 @@ define([
newTabElements.contentElement.setTabContentObserver();
// load all the structure elements for the new tab
newTabElements.contentElement.initContentStructure();
initContentStructure(newTabElements.contentElement);
tabsChanged = true;
@@ -671,7 +901,7 @@ define([
mapKeyTabSelector = 'nth-child(' + ( mapDataIndex + 1 ) + ')';
}
// ==============================================================
// ----------------------------------------------------------------------------------------------------
// this new created module
let tabContentElements = tabMapElement.find('.' + config.mapTabContentClass);
@@ -680,7 +910,7 @@ define([
tabContentElements.setTabContentObserver();
// load all the structure elements for ALL Tab Content Body
tabContentElements.initContentStructure();
initContentStructure(tabContentElements);
// set first Tab active
tabMapElement.find('.' + config.mapTabClass + ':' + mapKeyTabSelector + ' a').tab('show');

View File

@@ -120,8 +120,8 @@ define([
// load right menu
$('.' + config.pageSlidebarRightClass).loadRightMenu();
// set document observer for global events
setDocumentObserver();
// set page observer for global events
setPageObserver();
});
};
@@ -568,7 +568,7 @@ define([
/**
* catch all global document events
*/
let setDocumentObserver = function(){
let setPageObserver = function(){
// on "full-screen" change event
$(document).on('fscreenchange', function(e, state, elem){
@@ -696,6 +696,10 @@ define([
// END menu events =============================================================================
// global "modal" callback (for all modals)
$('body').on('hide.bs.modal', '> .modal', function(a,b) {
$(this).destroyTimestampCounter();
});
// update header links with current map data
$(document).on('pf:updateHeaderMapData', function(e, data){
@@ -783,45 +787,70 @@ define([
$.fn.updateHeaderUserData = function(){
let userData = Util.getCurrentUserData();
let userInfoElement = $('.' + config.headUserCharacterClass);
let currentCharacterId = userInfoElement.data('characterId');
let currentCharactersOptionIds = userInfoElement.data('characterOptionIds') ? userInfoElement.data('characterOptionIds') : [];
let newCharacterId = 0;
let newCharacterName = '';
let userInfoElement = $('.' + config.headUserCharacterClass);
let currentCharacterId = userInfoElement.data('characterId');
let currentCharactersOptionIds = userInfoElement.data('characterOptionIds') ? userInfoElement.data('characterOptionIds') : [];
let newCharacterId = 0;
let newCharacterName = '';
let userShipElement = $('.' + config.headUserShipClass);
let currentShipId = userShipElement.data('shipId');
let newShipId = 0;
let newShipName = '';
let userShipElement = $('.' + config.headUserShipClass);
let currentShipData = userShipElement.data('shipData');
let currentShipId = Util.getObjVal(currentShipData, 'typeId') || 0;
let newShipData = {
typeId: 0,
typeName: ''
};
// function for header element toggle animation
let animateHeaderElement = function(element, callback, triggerShow){
let animateHeaderElement = (element, callback, triggerShow) => {
let currentOpacity = parseInt(element.css('opacity'));
element.show().velocity('stop').velocity({
opacity: 0
},{
visibility : 'hidden',
duration: 500,
complete: function(){
// callback
callback();
let showHeaderElement = (element) => {
element.show().velocity({
opacity: [ 1, 0 ]
},{
// display: 'block',
visibility : 'visible',
duration: 1000
});
};
// show element
if(triggerShow === true){
element.velocity({
opacity: 1
}, {
visibility : 'visible',
duration: 500
});
}else{
// hide element
let hideHeaderElement = (element, callback) => {
element.velocity('stop').velocity({
opacity: [ 0, 1 ]
},{
// display: 'none',
visibility : 'hidden',
duration: 1000,
complete: function(){
element.hide();
// callback
callback($(this));
}
});
};
}
});
// run show/hide toggle in the correct order
if(currentOpacity > 0 && triggerShow){
// hide then show animation
hideHeaderElement(element, (element) => {
callback(element);
showHeaderElement(element);
});
}else if(currentOpacity > 0 && !triggerShow){
// hide animation
hideHeaderElement(element, (element) => {
element.hide();
callback(element);
});
}else if(currentOpacity === 0 && triggerShow){
// show animation
callback(element);
showHeaderElement(element);
}else{
// no animation
callback(element);
}
};
// check for character/ship changes ---------------------------------------------
@@ -833,8 +862,7 @@ define([
newCharacterName = userData.character.name;
if(userData.character.log){
newShipId = userData.character.log.ship.typeId;
newShipName = userData.character.log.ship.typeName;
newShipData = userData.character.log.ship;
}
// en/disable "map tracking" toggle
@@ -854,7 +882,7 @@ define([
}
// toggle element
animateHeaderElement(userInfoElement, function(){
animateHeaderElement(userInfoElement, (userInfoElement) => {
if(currentCharacterChanged){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + '/Character/' + newCharacterId + '_32.jpg' );
@@ -869,21 +897,21 @@ define([
}
// update user ship data --------------------------------------------------------
if(currentShipId !== newShipId){
if(currentShipId !== newShipData.typeId){
// set new data for next check
userShipElement.data('shipData', newShipData);
let showShipElement = true;
if(newShipId === 0){
showShipElement = false;
}
let showShipElement = newShipData.typeId > 0;
// toggle element
animateHeaderElement(userShipElement, function(){
userShipElement.find('span').text( newShipName );
userShipElement.find('img').attr('src', Init.url.ccpImageServer + '/Render/' + newShipId + '_32.png' );
animateHeaderElement(userShipElement, (userShipElement) => {
userShipElement.find('span').text( newShipData.typeName );
userShipElement.find('img').attr('src', Init.url.ccpImageServer + '/Render/' + newShipData.typeId + '_32.png' );
// trigger ship change event
$(document).trigger('pf:activeShip', {
shipData: newShipData
});
}, showShipElement);
// set new id for next check
userShipElement.data('shipId', newShipId);
}
};

View File

@@ -17,7 +17,7 @@ define(['jquery', 'mustache'], function($, Mustache) {
typeof config.functions === 'object' &&
typeof config.functions[functionName] === 'function'
){
config.functions[functionName]();
config.functions[functionName](config);
}
};
@@ -27,16 +27,13 @@ define(['jquery', 'mustache'], function($, Mustache) {
* @param data
*/
let showModule = function(config, data){
// require module template
requirejs(['text!templates/' + config.name + '.html'], function(template) {
// check for an id, if module already exists, do not insert again
if(
data.id === 'undefined' ||
$('#' + data.id).length === 0
){
let content = Mustache.render(template, data);
// display module
@@ -57,8 +54,6 @@ define(['jquery', 'mustache'], function($, Mustache) {
// init module function after render
initModule('after', config);
});
};

View File

@@ -0,0 +1,971 @@
/**
* connection info module
*/
define([
'jquery',
'app/init',
'app/util',
'app/map/util'
], ($, Init, Util, MapUtil) => {
'use strict';
let config = {
// module info
modulePosition: 1,
moduleName: 'connectionInfo',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
headUserShipClass: 'pf-head-user-ship', // class for "user settings" link
// connection info module
moduleTypeClass: 'pf-connection-info-module', // class for this module
// headline toolbar
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon
moduleHeadlineIconCurrentMassClass: 'pf-module-icon-button-mass', // class for "current ship mass" toggle icon
connectionInfoPanelClass: 'pf-connection-info-panel', // class for connection info panels
connectionInfoPanelId: 'pf-connection-info-panel-', // id prefix for connection info panels
// info table
moduleTableClass: 'pf-module-table', // class for module tables
connectionInfoTableLabelSourceClass: 'pf-connection-info-label-source', // class for source label
connectionInfoTableLabelTargetClass: 'pf-connection-info-label-target', // class for target label
connectionInfoTableRowMassShipClass: 'pf-connection-info-row-mass-ship', // class for "current ship mass" table row
connectionInfoTableCellMassTotalTooltipClass: 'pf-connection-info-mass-total-tooltip', // class for "mass total tooltip" table cell
connectionInfoTableCellMassTotalClass: 'pf-connection-info-mass-total', // class for "mass total" table cell
connectionInfoTableCellMassLogClass: 'pf-connection-info-mass-log', // class for "mass log" table cell
connectionInfoTableCellMassShipClass: 'pf-connection-info-mass-ship', // class for "current ship mass" table cell
connectionInfoTableCellMassLeftClass: 'pf-connection-info-mass-left', // class for "mass left" table cell
connectionInfoTableTooltipIconClass: 'pf-connection-info-tooltip-icon', // class for "tooltip" icon
connectionInfoTableWarningIconClass: 'pf-connection-info-warning-icon', // class for "warning" icon
// dataTable
connectionInfoTableClass: 'pf-connection-info-table', // class for connection tables
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells
// config
showShip: true // default for "show current ship mass" toggle
};
/**
* get module toolbar element
* @returns {*|jQuery|HTMLElement|void}
*/
let getHeadlineToolbar = () => {
let headlineToolbar = $('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-male',
config.showShip ? 'active' : '' ,
config.moduleHeadlineIconClass,
config.moduleHeadlineIconCurrentMassClass].join(' '),
title: 'toggle&nbsp;current&nbsp;ship&nbsp;mass'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fa', 'fa-fw', 'fa-refresh',
config.moduleHeadlineIconClass,
config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip')
);
headlineToolbar.find('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
return headlineToolbar;
};
/**
* get new connection element
* @param mapId
* @param connectionId
* @returns {jQuery}
*/
let getConnectionElement = (mapId, connectionId) => {
let connectionElement = $('<div>', {
id: getConnectionElementId(connectionId),
class: ['col-xs-12', 'col-sm-4', 'col-lg-3' , config.connectionInfoPanelClass].join(' ')
}).data({
mapId: mapId,
connectionId: connectionId
});
return connectionElement;
};
/**
* get info control panel element
* @param mapId
* @returns {void|jQuery|*}
*/
let getInfoPanelControl = (mapId) => {
let connectionElement = getConnectionElement(mapId, 0).append($('<div>', {
class: 'pf-dynamic-area',
html: '<i class="fa fa-fw fa-plus"></i>&nbsp;add connection&nbsp;&nbsp;<kbd>ctrl</kbd>&nbsp;+&nbsp;<kbd>click</kbd>'
}));
return connectionElement;
};
/**
* get connection information element
* @param connectionData
* @returns {void|*|jQuery|HTMLElement}
*/
let getInformationElement = (connectionData) => {
// connection scope -----------------------------------------------------------------------
let scopeLabel = MapUtil.getScopeInfoForConnection(connectionData.scope, 'label');
// connection type (dummy) classes --------------------------------------------------------
let connectionClasses = ['pf-fake-connection'];
for(let i = 0; i < connectionData.type.length; i++){
connectionClasses.push( MapUtil.getConnectionInfo( connectionData.type[i], 'cssClass') );
}
let massLog = 0;
let element = $('<div>', {
class: 'pf-dynamic-area'
}).append(
$('<table>', {
class: ['table', 'table-condensed', 'pf-table-fixed', config.moduleTableClass].join(' ')
}).data('showShip', config.showShip).append(
$('<thead>').append(
$('<tr>').append(
$('<th>', {
class: ['pf-table-cell-20', 'text-right', 'pf-help', 'pf-pie-chart'].join(' ')
}).attr('data-toggle', 'tooltip').attr('data-percent', '-100').easyPieChart({
barColor: (percent) => {
let color = '#e28a0d';
if((percent * -1) >= 100){
color = '#a52521';
}
return color;
},
overrideOptions: 'signed',
trackColor: '#5cb85c',
size: 14,
scaleColor: false,
lineWidth: 2,
lineCap: 'butt',
animate: false
}),
$('<th>', {
class: ['text-right'].join(' ')
}).attr('colspan', 2).append(
$('<span>', {
class: 'pf-link',
html: connectionData.sourceAlias + '&nbsp;&nbsp;'
}).on('click', function(){
Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: connectionData.source });
}),
$('<span>', {
class: [config.connectionInfoTableLabelSourceClass].join(' ')
}),
$('<i>', {
class: 'fa fa-fw fa-angle-double-right'
}),
$('<span>', {
class: [config.connectionInfoTableLabelTargetClass].join(' ')
}),
$('<span>', {
class: 'pf-link',
html: '&nbsp;&nbsp;' + connectionData.targetAlias
}).on('click', function(){
Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: connectionData.target });
})
)
)
),
$('<tbody>').append(
$('<tr>').append(
$('<td>', {
class: ['text-right', 'pf-help', config.connectionInfoTableCellMassTotalTooltipClass].join(' '),
html: '<i class="fa fa-fw fa-question-circle"></i>'
}),
$('<td>', {
text: scopeLabel.charAt(0).toUpperCase() + scopeLabel.slice(1)
}),
$('<td>', {
class: ['text-right'].join(' ')
}).append(
$('<div>', {
class: connectionClasses.join(' ')
})
)
),
$('<tr>').append(
$('<td>', {
class: ['text-right', 'pf-help'].join(' '),
html: '<i class="fa fa-fw fa-question-circle"></i>',
title: 'initial mass. From signature table'
}).attr('data-toggle', 'tooltip'),
$('<td>', {
text: 'Total mass'
}),
$('<td>', {
class: ['text-right', 'txt-color', config.connectionInfoTableCellMassTotalClass].join(' ')
})
),
$('<tr>').append(
$('<td>', {
class: ['text-right', 'pf-help'].join(' '),
html: '<i class="fa fa-fw fa-question-circle"></i>',
title: 'recorded total jump mass'
}).attr('data-toggle', 'tooltip'),
$('<td>', {
text: 'Logged mass'
}),
$('<td>', {
class: ['text-right', config.connectionInfoTableCellMassLogClass].join(' ')
})
),
$('<tr>', {
class: config.connectionInfoTableRowMassShipClass
}).append(
$('<td>', {
class: ['text-right', 'pf-help'].join(' '),
title: 'current ship mass'
}).attr('data-toggle', 'tooltip').append(
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-question-circle',
config.connectionInfoTableTooltipIconClass
].join(' ')
}),
$('<i>', {
class: [
'fa', 'fa-fw', 'fa-exclamation-triangle',
'txt-color', 'txt-color-danger',
'hidden', config.connectionInfoTableWarningIconClass
].join(' ')
})
),
$('<td>', {
class: ['pf-table-cell-ellipses-auto'].join(' '),
text: 'Ship mass'
}),
$('<td>', {
class: ['text-right', 'txt-color', config.connectionInfoTableCellMassShipClass].join(' ')
})
),
$('<tr>').append(
$('<td>', {
class: ['text-right', 'pf-help'].join(' '),
html: '<i class="fa fa-fw fa-question-circle"></i>',
title: 'max. mass left'
}).attr('data-toggle', 'tooltip'),
$('<td>', {
text: 'Mass left'
}),
$('<td>', {
class: ['text-right', 'txt-color', config.connectionInfoTableCellMassLeftClass].join(' ')
})
)
)
).on('pf:updateInfoTable', function(e, data){
// update information table -------------------------------------------------------
let tableElement = $(this);
let connectionData = tableElement.data('connectionData');
if(connectionData){
if(connectionData.scope === 'wh'){
// update signature information -------------------------------------------
let sourceLabelElement = tableElement.find('.' + config.connectionInfoTableLabelSourceClass);
let targetLabelElement = tableElement.find('.' + config.connectionInfoTableLabelTargetClass);
// get related jsPlumb connection
let connection = $().getConnectionById(data.mapId, data.connectionId);
let signatureTypeNames = MapUtil.getConnectionDataFromSignatures(connection, connectionData);
let sourceLabel = signatureTypeNames.sourceLabels;
let targetLabel = signatureTypeNames.targetLabels;
sourceLabelElement.html(MapUtil.getEndpointOverlayContent(sourceLabel));
targetLabelElement.html(MapUtil.getEndpointOverlayContent(targetLabel));
// remove K162
sourceLabel.diff(['K162']);
targetLabel.diff(['K162']);
// get static wormhole data by endpoint Labels
let wormholeName = '';
let wormholeData = null;
if(sourceLabel.length === 1 && targetLabel.length === 0){
wormholeName = sourceLabel[0];
}else if(sourceLabel.length === 0 && targetLabel.length === 1){
wormholeName = targetLabel[0];
}
if(
wormholeName &&
Init.wormholes.hasOwnProperty(wormholeName)
){
wormholeData = Object.assign({}, Init.wormholes[wormholeName]);
wormholeData.class = Util.getSecurityClassForSystem(wormholeData.security);
// init wormhole tooltip ----------------------------------------------
let massTotalTooltipCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalTooltipClass);
massTotalTooltipCell.addWormholeInfoTooltip(wormholeData);
}
// all required data is set -> re-calculate rows
tableElement.data('wormholeData', wormholeData);
tableElement.trigger('pf:calcInfoTable');
}
}
}).on('pf:calcInfoTable', function(e){
// re-calculate information table from .data() cell values ------------------------
let tableElement = $(this);
let massChartCell = tableElement.find('[data-percent]');
let wormholeData = tableElement.data('wormholeData');
let shipData = null;
let shipName = '';
let showShip = Boolean(tableElement.data('showShip'));
let massShipRow = tableElement.find('.' + config.connectionInfoTableRowMassShipClass);
// icons
let massShipTooltipIcon = massShipRow.find('.' + config.connectionInfoTableTooltipIconClass);
let massShipWarningIcon = massShipRow.find('.' + config.connectionInfoTableWarningIconClass);
// table cells
let massTotalCell = tableElement.find('.' + config.connectionInfoTableCellMassTotalClass);
let massLogCell = tableElement.find('.' + config.connectionInfoTableCellMassLogClass);
let massShipCell = tableElement.find('.' + config.connectionInfoTableCellMassShipClass);
let massLeftCell = tableElement.find('.' + config.connectionInfoTableCellMassLeftClass);
let massTotal = null; // initial connection mass
let massLog = massLogCell.data('mass'); // recorded mass
let massLogTotal = massLog; // recorded mass + current ship
let massIndividual = null; // mass mass per jump
let massShip = 0; // current ship
let massIndividualError = false;
// get wormhole data from signature binding ---------------------------------------
if(wormholeData){
massTotal = parseInt(wormholeData.massTotal);
massIndividual = parseInt(wormholeData.massIndividual);
}
// get current ship data ----------------------------------------------------------
massShipCell.parent().toggle(showShip);
if(showShip){
shipData = $('.' + config.headUserShipClass).data('shipData');
if(shipData){
if(shipData.mass){
massShip = parseInt(shipData.mass);
// check individual mass jump
if(massIndividual){
massIndividualError = massShip > massIndividual;
}
}
if(shipData.typeId && shipData.typeName){
shipName = shipData.typeName;
}
}
}
// update ship mass and "individual mass" cells ----------------------------------
massShipTooltipIcon.toggleClass('hidden', massIndividualError);
massShipWarningIcon.toggleClass('hidden', !massIndividualError);
let shipMassTooltip = 'current ship mass ' + (shipName ? '"' + shipName + '"' : '');
if(massIndividualError){
shipMassTooltip = '"' + shipName + '" exceeds max jump mass for this connection: ' + Util.formatMassValue(massIndividual);
}else{
// current ship mass check is OK -> add to massLogTotal
massLogTotal += massShip;
}
massShipTooltipIcon.parent().attr('title', shipMassTooltip).tooltip('fixTitle');
// current ship mass --------------------------------------------------------------
massShipCell.html( function(){
let cell = $(this);
let value = '&nbsp;';
let error = false;
let textLineThrough = false;
if(massShip > 0){
value += Util.formatMassValue(massShip);
if(massIndividualError){
error = textLineThrough = true;
value = '&nbsp;&nbsp;' + value;
}else{
value = '-' + value;
}
}else{
error = true;
value = 'undefined';
}
// change cell style
cell.toggleClass('txt-color-red', error)
.toggleClass('txt-color-warning', !error)
.toggleClass('pf-font-line-through', textLineThrough);
return value;
});
// calculate mass left ------------------------------------------------------------
let massLeft = massTotal - massLogTotal;
massLeft = (massLeft < 0) ? 0 : massLeft;
let massPercentLog = (massTotal > 0) ? Math.floor((100 / massTotal) * massLogTotal) : 0;
// update easyPieChart and tooltip ------------------------------------------------
let massPercentLeft = (100 - massPercentLog <= 0) ? 0 : '< ' + (100 - massPercentLog);
massChartCell.data('easyPieChart').enableAnimation().update(massPercentLog * -1);
massChartCell.attr('title', massPercentLeft + '% mass left').tooltip('fixTitle');
// update mass cells --------------------------------------------------------------
massTotalCell.html(massTotal > 0 ? Util.formatMassValue(massTotal) : 'undefined')
.toggleClass('txt-color-red', massTotal <= 0);
massLogCell.html('-&nbsp;' + Util.formatMassValue(massLog));
massLeftCell.html(
massLeft > 0 ?
'&#126;&nbsp;' + Util.formatMassValue(massLeft) :
(massLeft === 0 && massTotal) ?
'will collapse' : 'undefined')
.toggleClass('txt-color-red', massLeft <= 0)
.toggleClass('txt-color-success', massLeft > 0);
})
);
element.find('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
return element;
};
/**
* get HTML id by connectionId
* @param connectionId
* @returns {string}
*/
let getConnectionElementId = (connectionId) => {
return config.connectionInfoPanelId + connectionId;
};
/**
* get all visible connection panel elements
* @param moduleElement
* @returns {*|T|{}}
*/
let getConnectionElements = (moduleElement) => {
return moduleElement.find('.' + config.connectionInfoPanelClass).not('#' + getConnectionElementId(0));
};
/**
* request connection log data
* @param requestData
* @param context
* @param callback
*/
let requestConnectionLogData = (requestData, context, callback) => {
// show loading animation
for(let connectionId of requestData.connectionIds){
context.moduleElement.find('#' + getConnectionElementId(connectionId) + ' table').showLoadingAnimation();
}
$.ajax({
type: 'POST',
url: Init.path.getMapConnectionData,
data: requestData,
dataType: 'json',
context: context
}).done(function(connectionsData){
// enrich connectionData with "logs" data (if available) and other "missing" data
for(let i = 0; i < this.connectionsData.length; i++){
for(let connectionData of connectionsData) {
if(this.connectionsData[i].id === connectionData.id){
// copy some missing data
this.connectionsData[i].created = connectionData.created;
// check for mass logs and copy data
if(connectionData.logs && connectionData.logs.length){
this.connectionsData[i].logs = connectionData.logs;
}
// check for signatures and copy data
if(connectionData.signatures && connectionData.signatures.length){
this.connectionsData[i].signatures = connectionData.signatures;
}
break;
}
}
}
callback(this.moduleElement, this.connectionsData);
}).always(function(){
// hide loading animation
for(let contextData of this.connectionsData){
context.moduleElement.find('#' + getConnectionElementId(contextData.id) + ' table').hideLoadingAnimation();
}
});
};
/**
* @see requestConnectionLogData
* @param moduleElement
* @param mapId
* @param connectionsData
*/
let getConnectionsLogData = (moduleElement, mapId, connectionsData) => {
let connectionIds = [];
for(let connectionData of connectionsData) {
connectionIds.push(connectionData.id);
}
let requestData = {
mapId: mapId,
connectionIds: connectionIds,
addData : ['signatures', 'logs'],
// filterData : ['logs'] // do not exclude connections with NO "logs" -> sig data will be used as well
};
let contextData = {
moduleElement: moduleElement,
connectionsData: connectionsData
};
requestConnectionLogData(requestData, contextData, addConnectionsData);
};
/**
* replace/insert dataTables log data
* @param moduleElement
* @param connectionsData
*/
let addConnectionsData = (moduleElement, connectionsData) => {
let getRowIndexesByData = (dataTable, colName, value) => {
return dataTable.rows().eq(0).filter((rowIdx) => {
return (dataTable.cell(rowIdx, colName + ':name' ).data() === value);
});
};
for(let connectionData of connectionsData) {
// find related dom element for current connection
let connectionElement = moduleElement.find('#' + getConnectionElementId(connectionData.id));
if(connectionElement.length){
// attach connectionData to connection information for later use ------------------
let connectionInfoElement = connectionElement.find('.' + config.moduleTableClass);
connectionInfoElement.data('connectionData', connectionData);
// update dataTable ---------------------------------------------------------------
let dataTable = connectionElement.find('.dataTable').dataTable().api();
if(connectionData.logs && connectionData.logs.length > 0){
for(let i = 0; i < connectionData.logs.length; i++){
let rowData = connectionData.logs[i];
let row = null;
let animationStatus = null;
let indexes = getRowIndexesByData(dataTable, 'index', rowData.id);
if(indexes.length === 0){
// row not found -> add new row
row = dataTable.row.add( rowData );
animationStatus = 'added';
}
/* else{
// we DON´t expect changes -> no row update)
// update row with FIRST index
//row = dataTable.row( parseInt(indexes[0]) );
// update row data
//row.data(connectionData.logs[i]);
//animationStatus = 'changed';
} */
if(
animationStatus !== null &&
row.length > 0
){
row.nodes().to$().data('animationStatus', animationStatus);
}
}
}else{
// clear table or leave empty
dataTable.clear();
}
// redraw dataTable
dataTable.draw(false);
}
}
};
/**
*
* @param moduleElement
* @param mapId
* @param connectionData
*/
let updateConnectionPanel = (moduleElement, mapId, connectionData) => {
let rowElement = moduleElement.find('.row');
let connectionElement = rowElement.find('#' + getConnectionElementId(connectionData.id));
if( !connectionElement.length ){
connectionElement = getConnectionElement(mapId, connectionData.id);
connectionElement.append(getInformationElement(connectionData));
let table = $('<table>', {
class: ['compact', 'stripe', 'order-column', 'row-border', 'nowrap', config.connectionInfoTableClass].join(' ')
}).append('<tfoot><tr><th></th><th></th><th></th><th></th><th></th></tr></tfoot>');
connectionElement.append(table);
// init empty table
let logTable = table.DataTable({
pageLength: 8,
paging: true,
pagingType: 'simple',
lengthChange: false,
ordering: true,
order: [[ 4, 'desc' ]],
info: true,
searching: false,
hover: false,
autoWidth: false,
// rowId: 'systemTo',
language: {
emptyTable: 'No jumps recorded',
info: '_START_ to _END_ of _MAX_',
infoEmpty: ''
},
columnDefs: [
{
targets: 0,
name: 'index',
title: '<i class="fa fa-hashtag"></i>',
orderable: false,
searchable: false,
width: 20,
class: 'text-center',
data: 'id'
},{
targets: 1,
title: '',
width: 26,
orderable: false,
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
data: 'ship',
render: {
_: function(data, type, row){
let value = data.typeId;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + value + '_32.png" title="' + data.typeName + '" data-toggle="tooltip" />';
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).find('img').tooltip();
}
},{
targets: 2,
title: '',
width: 26,
orderable: false,
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
data: 'created.character',
render: {
_: function(data, type, row){
let value = data.name;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg" title="' + value + '" data-toggle="tooltip" />';
}
return value;
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).find('img').tooltip();
}
},{
targets: 3,
title: 'mass',
className: ['text-right'].join(' ') ,
data: 'ship.mass',
render: {
_: function(data, type, row){
let value = data;
if(type === 'display'){
value = Util.formatMassValue(value);
}
return value;
}
}
},{
targets: 4,
title: 'log',
width: 55,
className: ['text-right', config.tableCellCounterClass].join(' '),
data: 'created.created',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).initTimestampCounter('d');
}
}
],
drawCallback: function(settings){
let animationRows = this.api().rows().nodes().to$().filter(function(a,b ) {
return (
$(this).data('animationStatus') ||
$(this).data('animationTimer')
);
});
for(let i = 0; i < animationRows.length; i++){
$(animationRows[i]).pulseTableRow($(animationRows[i]).data('animationStatus'));
$(animationRows[i]).removeData('animationStatus');
}
},
footerCallback: function ( row, data, start, end, display ) {
let api = this.api();
let sumColumnIndexes = [3];
// column data for "sum" columns over this page
let pageTotalColumns = api
.columns( sumColumnIndexes, { page: 'all'} )
.data();
// sum columns for "total" sum
pageTotalColumns.each((colData, index) => {
pageTotalColumns[index] = colData.reduce((a, b) => {
return parseInt(a) + parseInt(b);
}, 0);
});
$(sumColumnIndexes).each((index, value) => {
$( api.column( value ).footer() ).text( Util.formatMassValue(pageTotalColumns[index]) );
// save mass for further reCalculation of "info" table
connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]);
});
// calculate "info" table -----------------------------------------------------
connectionElement.find('.' + config.moduleTableClass).trigger('pf:updateInfoTable', connectionElement.data());
}
});
// find position to insert
connectionElement.insertBefore(rowElement.find('#' + getConnectionElementId(0)));
logTable.on('order.dt search.dt', function(){
let pageInfo = logTable.page.info();
logTable.column(0, {search:'applied', order:'applied'}).nodes().each((cell, i) => {
let content = (pageInfo.recordsTotal - i) + '.&nbsp;&nbsp;';
$(cell).html(content);
});
});
logTable.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
}
};
/**
* remove connection Panel from moduleElement
* @param connectionElement
*/
let removeConnectionPanel = (connectionElement) => {
connectionElement = $(connectionElement);
if(connectionElement.length){
// destroy dataTable (and remove table from DOM)
let logTable = connectionElement.find('.' + config.connectionInfoTableClass);
logTable.dataTable().api().destroy(true);
// remove belonging connectionElement
connectionElement.remove();
}
};
/**
* get connections from ModuleElement
* -> validate with current map data
* @param moduleElement
* @param mapId
* @returns {{connectionsDataUpdate: Array, connectionsDataRemove: Array}}
*/
let getConnectionsDataFromModule = (moduleElement, mapId) => {
let activeMap = Util.getMapModule().getActiveMap();
let mapData = activeMap.getMapDataFromClient({forceData: true});
let connectionsData = {
connectionsDataUpdate: [],
connectionsDataRemove: [],
};
if(mapData !== false){
getConnectionElements(moduleElement).each((i, connectionElement) => {
let removeConnectionPanel = true;
let connectionData = {id: $(connectionElement).data('connectionId') };
let connection = $().getConnectionById(mapId, connectionData.id);
if(connection){
let connectionDataTemp = MapUtil.getDataByConnection(connection);
if(connectionDataTemp.id > 0){
// connection still on map - OK
removeConnectionPanel = false;
connectionData = connectionDataTemp;
}
}
if(removeConnectionPanel){
connectionsData.connectionsDataRemove.push(connectionData);
}else{
connectionsData.connectionsDataUpdate.push(connectionData);
}
});
}
return connectionsData;
};
/**
* update/init multiple connection panels at once
* @param moduleElement
* @param mapId
* @param connectionsDataUpdate
* @param connectionsDataRemove
*/
let updateConnectionPanels = (moduleElement, mapId, connectionsDataUpdate, connectionsDataRemove) => {
for(let connectionData of connectionsDataRemove){
let connectionElement = moduleElement.find('#' + getConnectionElementId(connectionData.id));
removeConnectionPanel(connectionElement);
}
for(let connectionData of connectionsDataUpdate){
updateConnectionPanel(moduleElement, mapId, connectionData);
}
// request connectionsLogData for each updated connection
if(connectionsDataUpdate.length){
getConnectionsLogData(moduleElement, mapId, connectionsDataUpdate);
}
// remove module if no connection panel left
// --> all connection deselected on map
let connectionElements = getConnectionElements(moduleElement);
if(connectionElements.length === 0){
MapUtil.getTabContentElementByMapElement(moduleElement).trigger('pf:removeConnectionModules');
}
// hide "control" panel when multiple connection
moduleElement.find('#' + getConnectionElementId(0)).toggle(connectionElements.length < 2);
};
/**
* set module observer
* @param moduleElement
* @param mapId
*/
let setModuleObserver = (moduleElement, mapId) => {
$(document).off('pf:updateConnectionInfoModule').on('pf:updateConnectionInfoModule', function(e, data){
updateConnectionPanels(
moduleElement,
data.mapId,
MapUtil.getDataByConnections(data.connectionsUpdate),
MapUtil.getDataByConnections(data.connectionsRemove)
);
});
$(document).off('pf:activeShip').on('pf:activeShip', function(e){
moduleElement.find('.' + config.connectionInfoPanelClass).each((i, connectionElement) => {
$(connectionElement).find('.' + config.moduleTableClass).each((i, tableElement) => {
$(tableElement).trigger('pf:calcInfoTable');
});
});
});
// init toggle active ship ----------------------------------------------------------------
moduleElement.find('.' + config.moduleHeadlineIconCurrentMassClass).on('click', function(e){
let currentMassIcon = $(this).toggleClass('active');
moduleElement.find('.' + config.connectionInfoPanelClass).each((i, connectionElement) => {
$(connectionElement).find('.' + config.moduleTableClass).each((i, tableElement) => {
$(tableElement).data('showShip', currentMassIcon.hasClass('active')).trigger('pf:calcInfoTable');
});
});
});
// init refresh connections ---------------------------------------------------------------
moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){
refreshConnectionPanels(moduleElement, mapId);
});
};
/**
* refresh all connection panels in a module
* @param moduleElement
* @param mapId
*/
let refreshConnectionPanels = (moduleElement, mapId) => {
let connectionsData = getConnectionsDataFromModule(moduleElement, mapId);
updateConnectionPanels(moduleElement, mapId, connectionsData.connectionsDataUpdate, connectionsData.connectionsDataRemove);
};
/**
* before module destroy callback
* @param moduleElement
*/
let beforeDestroy = (moduleElement) => {
getConnectionElements(moduleElement).each((i, connectionElement) => {
removeConnectionPanel(connectionElement);
});
};
/**
* init callback
* @param moduleElement
* @param mapId
* @param connectionData
*/
let initModule = (moduleElement, mapId, connectionData) => {
setModuleObserver(moduleElement, mapId);
};
/**
* get module element
* @param parentElement
* @param mapId
* @param connections
* @returns {*|jQuery|HTMLElement}
*/
let getModule = (parentElement, mapId, connections) => {
// create new module container
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: 'Connection'
}),
getHeadlineToolbar()
)
);
let rowElement = $('<div>', {
class: 'row'
});
moduleElement.append(rowElement);
rowElement.append(getInfoPanelControl(mapId));
updateConnectionPanels(moduleElement, mapId, MapUtil.getDataByConnections(connections), []);
return moduleElement;
};
return {
config: config,
getModule: getModule,
initModule: initModule,
beforeDestroy: beforeDestroy
};
});

View File

@@ -9,7 +9,7 @@ define([
'use strict';
var config = {
let config = {
headerSystemsContainerId: 'pf-header-systems', // id for systems layer
headerSystemConnectorsId: 'pf-header-connectors', // id for connectors layer
headerConnectionsContainerId: 'pf-header-connections', // id for connections layer
@@ -26,9 +26,9 @@ define([
* draw systems layer
* @param callback
*/
var drawSystems = function(callback){
let drawSystems = function(callback){
var pathObj = {
let pathObj = {
systems: {
strokepath: [
// systems =======================================================================
@@ -98,11 +98,11 @@ define([
* draw connectors layer
* @param callback
*/
var drawConnectors = function(callback){
let drawConnectors = function(callback){
var connectorDuration = 150;
let connectorDuration = 150;
var pathObj = {
let pathObj = {
connectors: {
strokepath: [
// connectors ====================================================================
@@ -184,14 +184,14 @@ define([
* draw connections layer
* @param callback
*/
var drawConnections = function(callback){
let drawConnections = function(callback){
var connectionDuration = 250;
var connectionWidth = 8;
var connectionInnerWidth = 4;
var connectionBorderColor = '#63676A'; //gray
let connectionDuration = 250;
let connectionWidth = 8;
let connectionInnerWidth = 4;
let connectionBorderColor = '#63676A'; //gray
var pathObj = {
let pathObj = {
connections: {
strokepath: [
// connections ====================================================================
@@ -288,7 +288,7 @@ define([
* draw background layer
* @param callback
*/
var drawBackground = function(callback){
let drawBackground = function(callback){
$('#' + config.headerBackgroundContainerId + ' .' + config.headerSystemClass).velocity('transition.bounceUpIn', {
stagger: 150,
complete: function(){
@@ -304,7 +304,7 @@ define([
* @param callback
*/
$.fn.drawDemoMap = function(callback){
var canvasElement = $(this);
let canvasElement = $(this);
// draw systems

View File

@@ -11,7 +11,7 @@ define([
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
let config = {
// select character dialog
settingsDialogId: 'pf-settings-dialog', // id for "settings" dialog
settingsAccountContainerId: 'pf-settings-dialog-account', // id for the "account" container
@@ -37,14 +37,14 @@ define([
$.fn.showSettingsDialog = function(){
// check if there are other dialogs open
var openDialogs = Util.getOpenDialogs();
let openDialogs = Util.getOpenDialogs();
if(openDialogs.length > 0){
return false;
}
requirejs(['text!templates/dialog/settings.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
id: config.settingsDialogId,
settingsAccountContainerId: config.settingsAccountContainerId,
settingsShareContainerId: config.settingsShareContainerId,
@@ -56,9 +56,9 @@ define([
ccpImageServer: Init.url.ccpImageServer
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
var accountSettingsDialog = bootbox.dialog({
let accountSettingsDialog = bootbox.dialog({
title: 'Account settings',
message: content,
buttons: {
@@ -72,19 +72,19 @@ define([
callback: function() {
// get the current active form
var form = $('#' + config.settingsDialogId).find('form').filter(':visible');
let form = $('#' + config.settingsDialogId).find('form').filter(':visible');
// validate form
form.validator('validate');
// check whether the form is valid
var formValid = form.isValidForm();
let formValid = form.isValidForm();
if(formValid === true){
var tabFormValues = form.getFormValues();
let tabFormValues = form.getFormValues();
// send Tab data and store values
var requestData = {
let requestData = {
formData: tabFormValues
};
@@ -134,7 +134,7 @@ define([
}).fail(function( jqXHR, status, error) {
accountSettingsDialog.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveAccountSettings', text: reason, type: 'error'});
// set new captcha for any request
@@ -147,7 +147,7 @@ define([
if(jqXHR.status === 500){
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
let errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
@@ -172,11 +172,11 @@ define([
// after modal is shown =======================================================================
accountSettingsDialog.on('shown.bs.modal', function(e) {
var dialogElement = $(this);
var form = dialogElement.find('form');
let dialogElement = $(this);
let form = dialogElement.find('form');
// request captcha image and show
var captchaImageWrapperContainer = $('#' + config.captchaImageWrapperId);
let captchaImageWrapperContainer = $('#' + config.captchaImageWrapperId);
captchaImageWrapperContainer.showCaptchaImage(config.captchaKeyUpdateAccount);
// init captcha refresh button
@@ -200,7 +200,7 @@ define([
off: 'Disable&nbsp;<i class="fa fa-fw fa-ban"></i>',
onstyle: 'success',
offstyle: 'warning',
width: 90,
width: 100,
height: 30
});

View File

@@ -12,7 +12,7 @@ define([
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
let config = {
// jump info dialog
creditsDialogClass: 'pf-credits-dialog', // class for credits dialog
creditsDialogLogoContainerId: 'pf-logo-container' // id for logo element
@@ -25,14 +25,14 @@ define([
requirejs(['text!templates/dialog/credit.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
logoContainerId: config.creditsDialogLogoContainerId,
version: Util.getVersion()
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
var creditDialog = bootbox.dialog({
let creditDialog = bootbox.dialog({
className: config.creditsDialogClass,
title: 'Licence',
message: content

View File

@@ -10,7 +10,7 @@ define([
], function($, Init, Util, bootbox) {
'use strict';
var config = {
let config = {
// global dialog
deleteAccountId: 'pf-dialog-delete-account', // dialog id
@@ -27,16 +27,16 @@ define([
requirejs(['text!templates/dialog/delete_account.html', 'mustache'], function(template, Mustache) {
var data = {
let data = {
deleteAccountId: config.deleteAccountId,
userData: Util.getCurrentUserData(),
captchaImageWrapperId: config.captchaImageWrapperId,
formErrorContainerClass: Util.config.formErrorContainerClass
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
var deleteAccountDialog = bootbox.dialog({
let deleteAccountDialog = bootbox.dialog({
title: 'Delete account',
message: content,
buttons: {
@@ -48,20 +48,20 @@ define([
label: '<i class="fa fa-user-times fa-fw"></i>&nbsp;delete account',
className: 'btn-danger',
callback: function() {
var dialogElement = $(this);
var form = dialogElement.find('form');
let dialogElement = $(this);
let form = dialogElement.find('form');
// validate form
form.validator('validate');
var formValid = form.isValidForm();
let formValid = form.isValidForm();
if(formValid){
var formValues = form.getFormValues();
let formValues = form.getFormValues();
if(! $.isEmptyObject(formValues) ){
// send Tab data and store values
var requestData = {
let requestData = {
formData: formValues
};
@@ -91,7 +91,7 @@ define([
}).fail(function( jqXHR, status, error) {
dialogElement.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': deleteAccount', text: reason, type: 'error'});
});

View File

@@ -8,31 +8,85 @@ define([
'app/util',
'app/render',
'bootbox',
], function($, Init, Util, Render, bootbox) {
], ($, Init, Util, Render, bootbox) => {
'use strict';
let config = {
// jump info dialog
jumpInfoDialogClass: 'pf-jump-info-dialog' // class for jump info dialog
jumpInfoDialogClass: 'pf-jump-info-dialog', // class for jump info dialog
wormholeInfoMassTableClass: 'pf-wormhole-info-mass-table', // class for "wormhole mass" table
wormholeInfoJumpTableClass: 'pf-wormhole-info-jump-table' // class for "wormhole jump" table
};
/**
* show jump info dialog
*/
$.fn.showJumpInfoDialog = function(){
requirejs(['text!templates/dialog/jump_info.html', 'mustache'], function(template, Mustache) {
let data = {};
requirejs(['text!templates/dialog/jump_info.html', 'mustache'], (template, Mustache) => {
let data = {
config: config,
wormholes: Object.keys(Init.wormholes).map(function(k) { return Init.wormholes[k]; }), // convert Json to array
securityClass: function(){
return function(value, render){
return this.Util.getSecurityClassForSystem( render(value) );
}.bind(this);
}.bind({
Util: Util
}),
massValue: function(){
return function(value, render){
let mass = render(value);
switch(mass.length){
case 0: return '';
case 1: return 'Yes';
default: return this.Util.formatMassValue(mass);
}
}.bind(this);
}.bind({
Util: Util
})
};
let content = Mustache.render(template, data);
let signatureReaderDialog = bootbox.dialog({
let jumpDialog = bootbox.dialog({
className: config.jumpInfoDialogClass,
title: 'Wormhole jump information',
message: content
message: content,
show: false
});
});
jumpDialog.on('show.bs.modal', function(e) {
// init dataTable
$(this).find('.' + config.wormholeInfoMassTableClass).DataTable({
pageLength: 25,
lengthMenu: [[10, 20, 25, 30, 40, -1], [10, 20, 25, 30, 40, 'All']],
autoWidth: false,
language: {
emptyTable: 'No wormholes',
zeroRecords: 'No wormholes found',
lengthMenu: 'Show _MENU_ wormholes',
info: 'Showing _START_ to _END_ of _TOTAL_ wormholes'
},
});
$(this).find('.' + config.wormholeInfoJumpTableClass).DataTable({
pageLength: -1,
paging: false,
lengthChange: false,
ordering: false,
searching: false,
info: false,
autoWidth: false,
language: {
emptyTable: 'No wormholes',
zeroRecords: 'No wormholes found',
lengthMenu: 'Show _MENU_ wormholes',
info: 'Showing _START_ to _END_ of _TOTAL_ wormholes'
},
});
});
jumpDialog.modal('show');
});
};
});

View File

@@ -184,7 +184,6 @@ define([
systemsElement.showLoadingAnimation(config.loadingOptions);
// table init complete
systemTable.on( 'init.dt', function () {
systemsElement.hideLoadingAnimation();
@@ -193,6 +192,10 @@ define([
tooltipElements.tooltip();
});
systemTable.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
// prepare data for dataTables
let systemsData = [];
for(let i = 0; i < mapData.data.systems.length; i++){
@@ -438,13 +441,6 @@ define([
data: 'updated',
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).initTimestampCounter();
// highlight cell
let diff = new Date().getTime() - cellData * 1000;
let dateDiff = new Date(diff);
if(dateDiff.getUTCDate() > 1){
$(cell).addClass('txt-color txt-color-warning');
}
}
},{
title: '',
@@ -545,7 +541,6 @@ define([
let connectionClasses = [];
for(let k = 0; k < tempConnectionData.type.length; k++){
connectionClasses.push( MapUtil.getConnectionInfo( tempConnectionData.type[k], 'cssClass') );
}
connectionClasses = connectionClasses.join(' ');
@@ -626,11 +621,13 @@ define([
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
$(cell).initTimestampCounter();
// highlight cell
let diff = new Date().getTime() - cellData * 1000;
let dateDiff = new Date(diff);
if(dateDiff.getUTCDate() > 1){
$(cell).addClass('txt-color txt-color-warning');
if(rowData.scope.scope_sort === 'wh'){
// highlight cell
let diff = new Date().getTime() - cellData * 1000;
let dateDiff = new Date(diff);
if(dateDiff.getUTCDate() > 1){
$(cell).addClass('txt-color txt-color-warning');
}
}
}
},{
@@ -1269,7 +1266,6 @@ define([
// load users table
usersElement.initUsersInfoTable(mapData);
});
// events for tab change

View File

@@ -11,7 +11,7 @@ define([
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
let config = {
releasesDialogClass: 'pf-releases-dialog' // class for "Releases" dialog
};
@@ -19,10 +19,10 @@ define([
* load release information in dialog
* @param releasesDialog
*/
var loadDialogData = function(releasesDialog){
let loadDialogData = function(releasesDialog){
// lock dialog
var dialogContent = releasesDialog.find('.modal-content');
let dialogContent = releasesDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
@@ -32,18 +32,18 @@ define([
dataType: 'json'
}).done(function(releasesData){
requirejs(['text!templates/ui/timeline_element.html', 'mustache'], function(template, Mustache) {
for(var i = 0; i < releasesData.length; i++){
var releaseData = releasesData[i];
for(let i = 0; i < releasesData.length; i++){
let releaseData = releasesData[i];
// template vars
var data = {
let data = {
isFirst: (i === 0),
isOdd: (i % 2 !== 0),
releaseDate: releaseData.published_at.substr(0, 10),
releaseData: releaseData
};
var content = Mustache.render(template, data);
let content = Mustache.render(template, data);
releasesDialog.find('ul.timeline').append(content);
}
@@ -55,7 +55,7 @@ define([
});
});
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + jqXHR.status + ': ' + error;
let reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
}).always(function() {
dialogContent.hideLoadingAnimation();
@@ -66,9 +66,9 @@ define([
* show releases dialog
*/
$.fn.releasesDialog = function(){
var content = '<ul class="timeline"></ul>';
let content = '<ul class="timeline"></ul>';
var releasesDialog = bootbox.dialog({
let releasesDialog = bootbox.dialog({
className: config.releasesDialogClass,
title: 'Releases',
size: 'large',

View File

@@ -12,14 +12,17 @@ define([
let config = {
// module info
moduleClass: 'pf-module', // class for each module
modulePosition: 3,
moduleName: 'systemGraph',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
// system graph module
systemGraphModuleClass: 'pf-system-graph-module', // class for this module
moduleTypeClass: 'pf-system-graph-module', // class for this module
systemGraphClass: 'pf-system-graph', // class for each graph
// system graph labels
systemGraphLabels: {
systemGraphs: {
jumps: {
headline: 'Jumps',
units: 'jumps',
@@ -55,9 +58,8 @@ define([
*/
let getInfoForGraph = function(graphKey, option){
let info = '';
if(config.systemGraphLabels.hasOwnProperty(graphKey)){
info = config.systemGraphLabels[graphKey][option];
if(config.systemGraphs.hasOwnProperty(graphKey)){
info = config.systemGraphs[graphKey][option];
}
return info;
@@ -68,9 +70,9 @@ define([
* @param graphElement
* @param graphKey
* @param graphData
* @param eventLine
*/
let initGraph = function(graphElement, graphKey, graphData, eventLine){
if(graphData.length > 0){
let labelYFormat = function(y){
return Math.round(y);
@@ -117,125 +119,130 @@ define([
};
/**
* draw graph module
* @param parentElement
* @param systemData
* request graphs data
* @param requestData
* @param context
* @param callback
*/
let drawModule = function(parentElement, systemData){
// graph data is available for k-space systems
if(systemData.type.id === 2){
let requestData = {
systemIds: [systemData.systemId]
};
// calculate time offset until system created
let serverData = Util.getServerTime();
let timestampNow = Math.floor(serverData.getTime() / 1000);
let timeSinceUpdate = timestampNow - systemData.updated;
let timeInHours = Math.floor(timeSinceUpdate / 3600);
let timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60);
let timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2);
let eventLine = timeInHours + timeInMinutesPercent;
// graph is from right to left -> convert event line
eventLine = 23 - eventLine;
$.ajax({
type: 'POST',
url: Init.path.getSystemGraphData,
data: requestData,
dataType: 'json'
}).done(function(systemGraphsData){
if( Object.keys(systemGraphsData).length > 0 ){
// create new (hidden) module container
let moduleElement = $('<div>', {
class: [config.moduleClass, config.systemGraphModuleClass].join(' '),
css: {opacity: 0}
});
// insert at the correct position
if($(parentElement).children().length === 1){
$(parentElement).append(moduleElement);
}else{
$(parentElement).find('>:first-child').after(moduleElement);
}
// row element
let rowElement = $('<div>', {
class: 'row'
});
moduleElement.append(rowElement);
$.each(systemGraphsData, function(systemId, graphsData){
$.each(graphsData, function(graphKey, graphData){
let colElement = $('<div>', {
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
});
let headlineElement = $('<h5>').text( getInfoForGraph(graphKey, 'headline') );
colElement.append(headlineElement);
let graphElement = $('<div>', {
class: config.systemGraphClass
});
colElement.append(graphElement);
rowElement.append(colElement);
initGraph(graphElement, graphKey, graphData, eventLine);
});
});
moduleElement.append($('<div>', {
css: {'clear': 'both'}
}));
// show module
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule
});
}
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
}
let requestGraphData = (requestData, context, callback) => {
// show loading animation
context.moduleElement.find('.' + config.systemGraphClass).showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.getSystemGraphData,
data: requestData,
dataType: 'json',
context: context
}).done(function(systemGraphsData){
callback(this, systemGraphsData);
}).fail(function( jqXHR, status, error) {
let reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
this.moduleElement.hide();
}).always(function(){
// hide loading animation
context.moduleElement.find('.' + config.systemGraphClass).hideLoadingAnimation();
});
};
/**
* main module load function
* @param systemData
* update graph elements with data
* @param context
* @param systemGraphsData
*/
$.fn.drawSystemGraphModule = function(systemData){
let addGraphData = (context, systemGraphsData) => {
let parentElement = $(this);
// calculate time offset until system created -----------------------------------------------------------------
let serverData = Util.getServerTime();
let timestampNow = Math.floor(serverData.getTime() / 1000);
let timeSinceUpdate = timestampNow - context.systemData.updated.updated;
// check if module already exists
let moduleElement = parentElement.find('.' + config.systemGraphModuleClass);
let timeInHours = Math.floor(timeSinceUpdate / 3600);
let timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60);
let timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2);
let eventLine = timeInHours + timeInMinutesPercent;
if(moduleElement.length > 0){
moduleElement.velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
drawModule(parentElement, systemData);
}
});
}else{
drawModule(parentElement, systemData);
// graph is from right to left -> convert event line
eventLine = 23 - eventLine;
// update graph data ------------------------------------------------------------------------------------------
for (let [systemId, graphsData] of Object.entries(systemGraphsData)){
for (let [graphKey, graphData] of Object.entries(graphsData)){
let graphElement = context.moduleElement.find('[data-graph="' + graphKey + '"]');
initGraph(graphElement, graphKey, graphData, eventLine);
}
}
};
/**
* @see requestGraphData
* @param moduleElement
* @param mapId
* @param systemData
*/
let updateGraphPanel = (moduleElement, mapId, systemData) => {
let requestData = {
systemIds: [systemData.systemId]
};
let contextData = {
moduleElement: moduleElement,
systemData: systemData
};
requestGraphData(requestData, contextData, addGraphData);
};
/**
* get module element
* @param parentElement
* @param mapId
* @param systemData
* @returns {*}
*/
let getModule = (parentElement, mapId, systemData) => {
// graph data is available for k-space systems
let moduleElement = null;
if(systemData.type.id === 2){
moduleElement = $('<div>');
let rowElement = $('<div>', {
class: 'row'
});
for (let [graphKey, graphConfig] of Object.entries(config.systemGraphs)){
rowElement.append(
$('<div>', {
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
}).append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: getInfoForGraph(graphKey, 'headline')
})
),
$('<div>', {
class: config.systemGraphClass
}).attr('data-graph', graphKey)
)
);
}
moduleElement.append(rowElement);
updateGraphPanel(moduleElement, mapId, systemData);
}
return moduleElement;
};
return {
config: config,
getModule: getModule
};
});

View File

@@ -13,10 +13,11 @@ define([
let config = {
// module info
moduleClass: 'pf-module', // class for each module
modulePosition: 2,
moduleName: 'systemInfo',
// system info module
systemInfoModuleClass: 'pf-system-info-module', // module wrapper
moduleTypeClass: 'pf-system-info-module', // class for this module
// breadcrumb
constellationLinkClass: 'pf-system-info-constellation', // class for "constellation" name
@@ -24,7 +25,7 @@ define([
typeLinkClass: 'pf-system-info-type', // class for "type" name
// info table
systemInfoTableClass: 'pf-system-info-table', // class for system info table
systemInfoTableClass: 'pf-module-table', // class for system info table
systemInfoNameInfoClass: 'pf-system-info-name', // class for "name" information element
systemInfoEffectInfoClass: 'pf-system-info-effect', // class for "effect" information element
systemInfoStatusLabelClass: 'pf-system-info-status-label', // class for "status" information element
@@ -54,7 +55,6 @@ define([
* set module observer and look for relevant system data to update
*/
let setModuleObserver = function(moduleElement){
$(document).off('pf:updateSystemInfoModule').on('pf:updateSystemInfoModule', function(e, data){
if(data){
moduleElement.updateSystemInfoModule(data);
@@ -197,24 +197,19 @@ define([
};
/**
*
* get module element
* @param parentElement
* @param mapId
* @param systemData
*/
let drawModule = function(parentElement, mapId, systemData){
let getModule = function(parentElement, mapId, systemData){
// create new module container
let moduleElement = $('<div>', {
class: [config.moduleClass, config.systemInfoModuleClass].join(' '),
css: {opacity: 0}
});
let moduleElement = $('<div>');
// store systemId -> module can be updated with the correct data
moduleElement.data('id', systemData.id);
parentElement.prepend(moduleElement);
// shattered wormhole info data
let shatteredWormholeInfo = false;
@@ -240,8 +235,8 @@ define([
position: moduleElement,
link: 'append',
functions: {
after: function(){
let tempModuleElement = parentElement.find('.' + config.systemInfoModuleClass);
after: function(conf){
let tempModuleElement = conf.position;
// lock "description" field until first update
tempModuleElement.find('.' + config.descriptionArea).showLoadingAnimation();
@@ -426,7 +421,6 @@ define([
return 'Loading...';
}
showModule(moduleElement);
}
}
};
@@ -464,52 +458,40 @@ define([
};
Render.showModule(moduleConfig, moduleData);
return moduleElement;
};
/**
* show system info module with animation
* init callback
* @param moduleElement
*/
let showModule = function(moduleElement){
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule,
complete: function(){
// set module observer
setModuleObserver(moduleElement);
// enable auto update
disableModuleUpdate = false;
}
});
};
/**
* update system info module
* @param mapId
* @param systemData
*/
$.fn.drawSystemInfoModule = function(mapId, systemData){
let initModule = function(moduleElement, mapId, systemData){
// set module observer
setModuleObserver(moduleElement);
let parentElement = $(this);
// check if module already exists
let moduleElement = parentElement.find('.' + config.systemInfoModuleClass);
if(moduleElement.length > 0){
moduleElement.velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
drawModule(parentElement, mapId, systemData);
}
});
}else{
drawModule(parentElement, mapId, systemData);
}
// enable auto update
disableModuleUpdate = false;
};
/**
* efore module destroy callback
* @param moduleElement
*/
let beforeDestroy = (moduleElement) => {
// remove xEditable description textarea
let descriptionTextareaElement = moduleElement.find('.' + config.descriptionTextareaElementClass);
descriptionTextareaElement.editable('destroy');
};
return {
config: config,
getModule: getModule,
initModule: initModule,
beforeDestroy: beforeDestroy
};
});

View File

@@ -8,13 +8,16 @@ define([
let config = {
// module info
moduleClass: 'pf-module', // class for each module
modulePosition: 2,
moduleName: 'systemKillboard',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
// headline toolbar
systemModuleHeadlineIcon: 'pf-module-icon-button', // class for toolbar icons in the head
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
// system killboard module
systemKillboardModuleClass: 'pf-system-killboard-module', // module wrapper
moduleTypeClass: 'pf-system-killboard-module', // class for this module
systemKillboardGraphKillsClass: 'pf-system-killboard-graph-kills', // class for system kill graph
// system killboard list
@@ -178,34 +181,8 @@ define([
* @param systemData
*/
$.fn.updateSystemInfoGraphs = function(systemData){
let moduleElement = $(this);
// headline toolbar icons
let headlineToolbar = $('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-external-link ', config.systemModuleHeadlineIcon].join(' '),
title: 'zkillboard.com'
}).on('click', function(e){
window.open(
'//zkillboard.com/system/' + systemData.systemId,
'_blank'
);
}).attr('data-toggle', 'tooltip')
);
moduleElement.append(headlineToolbar);
// headline
let headline = $('<h5>', {
text: 'Killboard'
});
moduleElement.append(headline);
let killboardGraphElement = $('<div>', {
class: config.systemKillboardGraphKillsClass
});
@@ -222,14 +199,11 @@ define([
// private function draws a "system kills" graph
let drawGraph = function(data){
let tableData = data.tableData;
// change order (show right to left)
tableData.reverse();
if(data.count === 0){
labelOptions.type = 'label-success';
label = getLabel( 'No kills found within the last 24h', labelOptions );
@@ -247,6 +221,7 @@ define([
Morris.Bar({
element: killboardGraphElement,
resize: true,
redraw: true,
grid: true,
gridStrokeWidth: 0.3,
gridTextSize: 9,
@@ -418,64 +393,71 @@ define([
});
};
/**
* get module toolbar element
* @param systemData
* @returns {*|jQuery|HTMLElement|void}
*/
let getHeadlineToolbar = (systemData) => {
let headlineToolbar = $('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-external-link ', config.moduleHeadlineIconClass].join(' '),
title: 'zkillboard.com'
}).on('click', function(e){
window.open(
'//zkillboard.com/system/' + systemData.systemId,
'_blank'
);
}).attr('data-toggle', 'tooltip')
);
headlineToolbar.find('[data-toggle="tooltip"]').tooltip({
container: 'body'
});
return headlineToolbar;
};
/**
* before module "show" callback
* @param moduleElement
* @param systemData
*/
let beforeShow = (moduleElement, systemData) => {
// update graph
moduleElement.updateSystemInfoGraphs(systemData);
};
/**
* get module element
* @param parentElement
* @param systemData
* @returns {*|jQuery|HTMLElement}
*/
let getModule = function(parentElement, systemData){
let getModule = (parentElement, mapId, systemData) => {
// create new module container
let moduleElement = $('<div>', {
class: [config.moduleClass, config.systemKillboardModuleClass].join(' '),
css: {opacity: 0}
});
parentElement.append(moduleElement);
// update graph
moduleElement.updateSystemInfoGraphs(systemData);
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: 'Killboard'
}),
getHeadlineToolbar(systemData)
)
);
return moduleElement;
};
/**
* main module load function
* @param systemData
*/
$.fn.drawSystemKillboardModule = function(systemData){
let parentElement = $(this);
// show route module
let showModule = function(moduleElement){
if(moduleElement){
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule
});
}
};
// check if module already exists
let moduleElement = parentElement.find('.' + config.systemKillboardModuleClass);
if(moduleElement.length > 0){
moduleElement.velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
moduleElement = getModule(parentElement, systemData);
showModule(moduleElement);
}
});
}else{
moduleElement = getModule(parentElement, systemData);
showModule(moduleElement);
}
return {
config: config,
getModule: getModule,
beforeShow: beforeShow
};
});

View File

@@ -13,18 +13,21 @@ define([
let config = {
// module info
moduleClass: 'pf-module', // class for each module
modulePosition: 1,
moduleName: 'systemRoute',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
routeCacheTTL: 10, // route cache timer (client) in seconds
// system route module
systemRouteModuleClass: 'pf-system-route-module', // class for this module
moduleTypeClass: 'pf-system-route-module', // class for this module
// headline toolbar
systemModuleHeadlineIcon: 'pf-module-icon-button', // class for toolbar icons in the head
systemModuleHeadlineIconSearch: 'pf-module-icon-button-search', // class for "search" icon
systemModuleHeadlineIconSettings: 'pf-module-icon-button-settings', // class for "settings" icon
systemModuleHeadlineIconRefresh: 'pf-module-icon-button-refresh', // class for "refresh" icon
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
moduleHeadlineIconSearchClass: 'pf-module-icon-button-search', // class for "search" icon
moduleHeadlineIconSettingsClass: 'pf-module-icon-button-settings', // class for "settings" icon
moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon
systemSecurityClassPrefix: 'pf-system-security-', // prefix class for system security level (color)
@@ -35,7 +38,7 @@ define([
systemInfoRoutesTableClass: 'pf-system-route-table', // class for route tables
mapSelectId: 'pf-route-dialog-map-select', // id for "map" select
dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells
dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells
};
// cache for system routes
@@ -48,7 +51,7 @@ define([
* @param context
* @param routesData
*/
let callbackAddRouteRow = function(context, routesData){
let callbackAddRouteRow = (context, routesData) => {
if(routesData.length > 0){
for(let i = 0; i < routesData.length; i++){
@@ -86,7 +89,7 @@ define([
* @param rowData
* @returns {*}
*/
let addRow = function(context, rowData){
let addRow = (context, rowData) => {
let dataTable = context.dataTable;
let rowElement = null;
let row = null;
@@ -94,7 +97,7 @@ define([
// search for an existing row (e.g. on mass "table refresh" [all routes])
// get rowIndex where column 1 (equals to "systemToData.name") matches rowData.systemToData.name
let indexes = dataTable.rows().eq(0).filter( function (rowIdx) {
let indexes = dataTable.rows().eq(0).filter((rowIdx) => {
return (dataTable.cell(rowIdx, 1 ).data().name === rowData.systemToData.name);
});
@@ -129,8 +132,7 @@ define([
* @param context
* @param callback
*/
let getRouteData = function(requestData, context, callback){
let getRouteData = (requestData, context, callback) => {
context.moduleElement.showLoadingAnimation();
$.ajax({
@@ -145,7 +147,6 @@ define([
// execute callback
callback(this, routesData.routesData);
});
};
/**
@@ -153,7 +154,7 @@ define([
* @param moduleElement
* @param dataTable
*/
let updateRoutesTable = function(moduleElement, dataTable){
let updateRoutesTable = (moduleElement, dataTable) => {
let context = {
moduleElement: moduleElement,
dataTable: dataTable
@@ -172,7 +173,7 @@ define([
* @param {Object} rowData
* @returns {Object}
*/
let getRouteRequestDataFromRowData = function(rowData){
let getRouteRequestDataFromRowData = (rowData) => {
return {
mapIds: (rowData.hasOwnProperty('mapIds')) ? rowData.mapIds : [],
systemFromData: (rowData.hasOwnProperty('systemFromData')) ? rowData.systemFromData : {},
@@ -193,7 +194,7 @@ define([
* show route dialog. User can search for systems and jump-info for each system is added to a data table
* @param dialogData
*/
let showFindRouteDialog = function(dialogData){
let showFindRouteDialog = (dialogData) => {
let mapSelectOptions = [];
let currentMapData = Util.getCurrentMapData();
@@ -678,50 +679,45 @@ define([
};
/**
* get the route finder moduleElement
* get module element
* @returns {*}
*/
let getModule = function(){
// create new module container
let moduleElement = $('<div>', {
class: [config.moduleClass, config.systemRouteModuleClass].join(' ')
});
// headline toolbar icons
let headlineToolbar = $('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-search', config.systemModuleHeadlineIcon, config.systemModuleHeadlineIconSearch].join(' '),
title: 'find&nbsp;route'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fa', 'fa-fw', 'fa-sliders', config.systemModuleHeadlineIcon, config.systemModuleHeadlineIconSettings].join(' '),
title: 'settings'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fa', 'fa-fw', 'fa-refresh', config.systemModuleHeadlineIcon, config.systemModuleHeadlineIconRefresh].join(' '),
title: 'refresh&nbsp;all'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip')
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
class: 'pull-right'
}).append(
$('<i>', {
class: ['fa', 'fa-fw', 'fa-search', config.moduleHeadlineIconClass, config.moduleHeadlineIconSearchClass].join(' '),
title: 'find&nbsp;route'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fa', 'fa-fw', 'fa-sliders', config.moduleHeadlineIconClass, config.moduleHeadlineIconSettingsClass].join(' '),
title: 'settings'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
$('<i>', {
class: ['fa', 'fa-fw', 'fa-refresh', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
title: 'refresh&nbsp;all'
}).attr('data-html', 'true').attr('data-toggle', 'tooltip')
),
$('<h5>', {
text: 'Routes'
})
)
);
moduleElement.append(headlineToolbar);
// headline
let headline = $('<h5>', {
class: 'pull-left',
text: 'Routes'
});
moduleElement.append(headline);
// crate new route table
let table = $('<table>', {
class: ['compact', 'stripe', 'order-column', 'row-border', config.systemInfoRoutesTableClass].join(' ')
});
moduleElement.append( $(table) );
moduleElement.append(table);
// init empty table
let routesTable = table.DataTable( {
@@ -1006,12 +1002,12 @@ define([
let routesTable = routesTableElement.DataTable();
// init refresh routes --------------------------------------------------------------------
moduleElement.find('.' + config.systemModuleHeadlineIconRefresh).on('click', function(e){
moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){
updateRoutesTable(moduleElement, routesTable);
});
// init search routes dialog --------------------------------------------------------------
moduleElement.find('.' + config.systemModuleHeadlineIconSearch).on('click', function(e){
moduleElement.find('.' + config.moduleHeadlineIconSearchClass).on('click', function(e){
let maxRouteSearchLimit = this.Init.routeSearch.limit;
if(routesTable.rows().count() >= maxRouteSearchLimit){
@@ -1032,7 +1028,7 @@ define([
}));
// init settings dialog -------------------------------------------------------------------
moduleElement.find('.' + config.systemModuleHeadlineIconSettings).on('click', function(e){
moduleElement.find('.' + config.moduleHeadlineIconSettingsClass).on('click', function(e){
let dialogData = {
mapId: mapId
};
@@ -1061,49 +1057,10 @@ define([
};
/**
* updates an dom element with the system route module
* @param mapId
* @param systemData
*/
$.fn.drawSystemRouteModule = function(mapId, systemData){
let parentElement = $(this);
// show route module
let showModule = function(moduleElement){
if(moduleElement){
moduleElement.css({ opacity: 0 });
parentElement.append(moduleElement);
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule,
complete: function(){
initModule(moduleElement, mapId, systemData);
}
});
}
};
// check if module already exists
let moduleElement = parentElement.find('.' + config.systemRouteModuleClass);
if(moduleElement.length > 0){
moduleElement.velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
moduleElement = getModule();
showModule(moduleElement);
}
});
}else{
moduleElement = getModule();
showModule(moduleElement);
}
return {
config: config,
getModule: getModule,
initModule: initModule
};
});

View File

@@ -15,10 +15,15 @@ define([
let config = {
// module info
modulePosition: 4,
moduleName: 'systemSignature',
moduleHeadClass: 'pf-module-head', // class for module header
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
moduleClass: 'pf-module', // class for each module
// system signature module
systemSigModuleClass: 'pf-sig-table-module', // module wrapper
moduleTypeClass: 'pf-sig-table-module', // module wrapper
// tables
tableToolsClass: 'pf-table-tools', // class for table toolbar
@@ -31,6 +36,7 @@ define([
signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar
// toolbar
sigTableLazyToggleButtonClass: 'pf-sig-table-lazy-button', // class for "lazy update" toggle button
sigTableClearButtonClass: 'pf-sig-table-clear-button', // class for "clear" signatures button
// signature table
@@ -965,19 +971,19 @@ define([
if( clearButton.is(':hidden') ){
// show button
clearButton.velocity('transition.bounceIn', {
duration: 180
clearButton.velocity('transition.expandIn', {
duration: 100
});
}else{
// highlight button
clearButton.velocity('callout.pulse', {
duration: 240
duration: 200
});
}
}else{
// hide button
clearButton.velocity('transition.bounceOut', {
duration: 180
clearButton.velocity('transition.expandOut', {
duration: 100
});
}
};
@@ -1048,6 +1054,12 @@ define([
checkDeleteSignaturesButton(moduleElement);
}
})
).append(
$('<input>', {
type: 'checkbox',
class: [config.sigTableLazyToggleButtonClass, 'btn-labeled'].join(' '),
value: 1,
}).attr('data-toggle', 'toggle')
).append(
getLabledButton({
type: 'danger',
@@ -1073,6 +1085,24 @@ define([
moduleElement.append(tableToolbar);
// "lazy update" toggle button --------------------------------------------------------------------------------
let lazyToggleCheckbox = moduleElement.find('.' + config.sigTableLazyToggleButtonClass).bootstrapToggle({
size: 'small' ,
on: '<i class="fa fa-fw fa-exchange"></i>&nbsp;&nbsp;lazy&nbsp;delete',
off: '<i class="fa fa-fw fa-clipboard"></i>&nbsp;&nbsp;lazy&nbsp;update',
onstyle: 'warning' ,
offstyle: 'default' ,
width: 110
});
let lazyToggleButton = lazyToggleCheckbox.parent();
lazyToggleButton.find('.toggle-on').attr('title', 'lazy \'update\' and \'delete\' old<br>from clipboard |ctrl&nbsp;+&nbsp;v|');
lazyToggleButton.find('.toggle-off').attr('title', 'lazy \'update\' signatures<br>from clipboard |ctrl&nbsp;+&nbsp;v|');
lazyToggleButton.initTooltips({
container: 'body',
html: true
});
// add toolbar action for table -------------------------------------------------------------------------------
let tableToolbarAction = $('<div>', {
class: config.tableToolsActionClass
@@ -1469,11 +1499,11 @@ define([
let getSignatureConnectionOptions = (mapId, systemData) => {
let map = Map.getMapInstance( mapId );
let systemId = MapUtil.getSystemId(mapId, systemData.id);
let systemConnections = MapUtil.searchConnectionsBySystems(map, [systemId]);
let systemConnections = MapUtil.searchConnectionsBySystems(map, [systemId], 'wh');
let connectionOptions = [];
for(let i = 0; i < systemConnections.length; i++){
let connectionData = Map.getDataByConnection(systemConnections[i]);
let connectionData = MapUtil.getDataByConnection(systemConnections[i]);
// connectionId is required (must be stored)
if(connectionData.id){
@@ -1739,7 +1769,7 @@ define([
let deleteSignatures = function(tableApi, rows){
let deletedSignatures = 0;
let moduleElement = $('.' + config.systemSigModuleClass);
let moduleElement = $('.' + config.moduleTypeClass);
let data = rows.data();
let rowElements = rows.nodes().to$();
let signatureCount = data.length;
@@ -2312,12 +2342,35 @@ define([
checkDeleteSignaturesButton(e.data.moduleElement);
});
// destroy dataTables event -----------------------------------------------------------------------------------
tablePrimaryElement.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
signatureTableApi.on('destroy.dt', function(){
$(this).destroyTimestampCounter();
});
// event listener for global "paste" signatures into the page -------------------------------------------------
moduleElement.on('pf:updateSystemSignatureModuleByClipboard', function(e, clipboard){
$(this).updateSignatureTableByClipboard(systemData, clipboard, {});
// check "lazy update" toggle button
let signatureOptions = {
deleteOld: moduleElement.find('.' + config.sigTableLazyToggleButtonClass).is(':checked') ? 1 : 0
};
$(this).updateSignatureTableByClipboard(systemData, clipboard, signatureOptions);
});
};
/**
* init callback
* @param moduleElement
* @param mapId
* @param connectionData
*/
let initModule = (moduleElement, mapId, systemData) => {
unlockSignatureTable(true);
};
/**
* get module element
* @param parentElement
@@ -2326,25 +2379,23 @@ define([
* @returns {*|jQuery|HTMLElement}
*/
let getModule = function(parentElement, mapId, systemData){
// create new module container
let moduleElement = $('<div>', {
class: [config.moduleClass, config.systemSigModuleClass].join(' '),
css: {opacity: 0}
});
let moduleElement = $('<div>').append(
$('<div>', {
class: config.moduleHeadClass
}).append(
$('<h5>', {
class: config.moduleHandlerClass
}),
$('<h5>', {
text: 'Signatures'
})
)
);
moduleElement.data('mapId', mapId);
moduleElement.data('systemId', systemData.id);
// headline
let headline = $('<h5>', {
text: 'Signatures'
});
moduleElement.append(headline);
$(parentElement).append(moduleElement);
// init dataTables
initSignatureDataTable(systemData);
@@ -2385,76 +2436,31 @@ define([
};
/**
* main module load function
* @param mapId
* @param systemData
* before module reDraw callback
*/
$.fn.drawSignatureTableModule = function(mapId, systemData){
let parentElement = $(this);
let beforeReDraw = () => {
// disable update
lockSignatureTable();
};
// show module
let showModule = function(moduleElement){
if(moduleElement){
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule,
complete: function(){
unlockSignatureTable(true);
}
});
}
};
// some custom array functions
let initArrayFunctions = function(){
/**
* sort array of objects by property name
* @param p
* @returns {Array.<T>}
*/
Array.prototype.sortBy = function(p) {
return this.slice(0).sort(function(a,b) {
return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
});
};
};
// check if module already exists
let moduleElement = parentElement.find('.' + config.systemSigModuleClass);
if(moduleElement.length > 0){
// disable update
lockSignatureTable();
moduleElement.velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
tempElement = $(tempElement);
// Destroying the data tables throws
// save remove of all dataTables
let mapId = tempElement.data('mapId');
let systemId = tempElement.data('systemId');
deleteDataTableInstance(mapId, systemId, 'primary');
deleteDataTableInstance(mapId, systemId, 'secondary');
tempElement.remove();
moduleElement = getModule(parentElement, mapId, systemData);
// make modules appear "nice"
moduleElement.delay(150);
showModule(moduleElement);
}
});
}else{
// init array prototype functions
initArrayFunctions();
moduleElement = getModule(parentElement, mapId, systemData);
showModule(moduleElement);
}
/**
* before module destroy callback
*/
let beforeDestroy = (moduleElement) => {
// Destroying the data tables throws
// -> safety remove all dataTables
let mapId = moduleElement.data('mapId');
let systemId = moduleElement.data('systemId');
deleteDataTableInstance(mapId, systemId, 'primary');
deleteDataTableInstance(mapId, systemId, 'secondary');
};
return {
config: config,
getModule: getModule,
initModule: initModule,
beforeReDraw: beforeReDraw,
beforeDestroy: beforeDestroy,
getAllSignatureNamesBySystem: getAllSignatureNamesBySystem
};

View File

@@ -546,6 +546,8 @@ define([
}
}
return element;
};
/**
@@ -618,6 +620,17 @@ define([
popoverElement = button.data('bs.popover').tip();
popoverElement.velocity('transition.' + easeEffect, velocityOptions);
popoverElement.initTooltips();
// set click events. This is required to pass data to "beforeunload" events
// -> there is no way to identify the target within that event
popoverElement.on('click', '.btn', function(){
// character switch detected
$('body').data('characterSwitch', true);
// ... and remove "characterSwitch" data again! after "unload"
setTimeout(function() {
$('body').removeData('characterSwitch');
}, 500);
});
}else{
popoverElement = button.data('bs.popover').tip();
if(popoverElement.is(':visible')){
@@ -668,39 +681,6 @@ define([
});
};
/**
* add a wormhole tooltip with wh specific data to elements
* @param tooltipData
* @returns {*}
*/
$.fn.addWormholeInfoTooltip = function(tooltipData){
return this.each(function() {
let element = $(this);
requirejs(['text!templates/tooltip/wormhole_info.html', 'mustache'], function (template, Mustache) {
let content = Mustache.render(template, tooltipData);
element.popover({
placement: 'top',
html: true,
trigger: 'hover',
content: '',
container: 'body',
title: tooltipData.name +
'<span class="pull-right ' + tooltipData.class +'">' + tooltipData.security + '</span>',
delay: {
show: 250,
hide: 0
}
});
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
});
});
};
/**
* display a custom message (info/warning/error) to a container element
* check: $.fn.showFormMessage() for an other way of showing messages
@@ -862,6 +842,17 @@ define([
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
/**
* sort array of objects by property name
* @param p
* @returns {Array.<T>}
*/
Array.prototype.sortBy = function(p) {
return this.slice(0).sort((a,b) => {
return (a[p] > b[p]) ? 1 : (a[p] < b[p]) ? -1 : 0;
});
};
};
/**
@@ -883,7 +874,7 @@ define([
}
return flatten;
} ;
};
/**
* set default configuration for "Bootbox" dialogs
@@ -1081,6 +1072,28 @@ define([
return Init.currentUserData;
};
/**
* get either active characterID or characterId from initial page load
* @returns {number}
*/
let getCurrentCharacterId = () => {
let userData = getCurrentUserData();
let currentCharacterId = 0;
if(
userData &&
userData.character
){
currentCharacterId = parseInt( userData.character.id );
}
if(!currentCharacterId){
// no active character... -> get default characterId from initial page load
currentCharacterId = parseInt(document.body.getAttribute('data-character-id'));
}
return currentCharacterId;
};
/**
* get a unique ID for each tab
* -> store ID in session storage
@@ -1104,21 +1117,9 @@ define([
// Add custom application headers on "same origin" requests only!
// -> Otherwise a "preflight" request is made, which will "probably" fail
if(settings.crossDomain === false){
// Add browser tab information
xhr.setRequestHeader('Pf-Tab-Id', getBrowserTabId()) ;
// add current character data to ANY XHR request (HTTP HEADER)
// -> This helps to identify multiple characters on multiple browser tabs
let userData = getCurrentUserData();
let currentCharacterId = 0;
if(
userData &&
userData.character
){
currentCharacterId = parseInt( userData.character.id );
}
xhr.setRequestHeader('Pf-Character', currentCharacterId);
xhr.setRequestHeader('Pf-Character', getCurrentCharacterId());
}
}
});
@@ -1278,7 +1279,6 @@ define([
* @returns {*|HTMLElement}
*/
let getMapModule = function(){
let mapModule = $('#' + config.mapModuleId);
if(mapModule.length === 0){
mapModule = $('<div>', {
@@ -1470,13 +1470,11 @@ define([
* @param sec
* @returns {string}
*/
let getSecurityClassForSystem = function(sec){
let getSecurityClassForSystem = (sec) => {
let secClass = '';
if( Init.classes.systemSecurity.hasOwnProperty(sec) ){
secClass = Init.classes.systemSecurity[sec]['class'];
}
return secClass;
};
@@ -1994,7 +1992,7 @@ define([
* set currentSystemData as "global" variable
* @param systemData
*/
let setCurrentSystemData = function(systemData){
let setCurrentSystemData = (systemData) => {
Init.currentSystemData = systemData;
};
@@ -2002,7 +2000,7 @@ define([
* get currentSystemData from "global" variables
* @returns {*}
*/
let getCurrentSystemData = function(){
let getCurrentSystemData = () => {
return Init.currentSystemData;
};
@@ -2060,7 +2058,7 @@ define([
* @param price
* @returns {string}
*/
let formatPrice = function(price){
let formatPrice = (price) => {
price = Number( price ).toFixed(2);
let parts = price.toString().split('.');
@@ -2069,6 +2067,15 @@ define([
return price + ' ISK';
};
/**
* format mass value
* @param value
* @returns {string}
*/
let formatMassValue = (value) => {
return (parseInt(value) / 1000).toLocaleString() + ' t';
};
/**
* get localForage instance (singleton) for offline client site storage
* @returns {localforage}
@@ -2098,7 +2105,7 @@ define([
* @param date
* @returns {Date}
*/
let createDateAsUTC = function(date){
let createDateAsUTC = (date) => {
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
};
@@ -2107,7 +2114,7 @@ define([
* @param date
* @returns {Date}
*/
let convertDateToUTC = function(date){
let convertDateToUTC = (date) => {
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
};
@@ -2117,7 +2124,7 @@ define([
* @param showSeconds
* @returns {string}
*/
let convertDateToString = function(date, showSeconds){
let convertDateToString = (date, showSeconds) => {
let dateString = ('0'+ (date.getMonth() + 1 )).slice(-2) + '/' + ('0'+date.getDate()).slice(-2) + '/' + date.getFullYear();
let timeString = ('0' + date.getHours()).slice(-2) + ':' + ('0'+date.getMinutes()).slice(-2);
timeString += (showSeconds) ? ':' + ('0'+date.getSeconds()).slice(-2) : '';
@@ -2142,7 +2149,7 @@ define([
* -> www.pathfinder.com/pathfinder/ -> /pathfinder
* @returns {string|string}
*/
let getDocumentPath = function(){
let getDocumentPath = () => {
let pathname = window.location.pathname;
// replace file endings
let r = /[^\/]*$/;
@@ -2155,7 +2162,7 @@ define([
* @param url
* @param params
*/
let redirect = function(url, params){
let redirect = (url, params) => {
let currentUrl = document.URL;
if(url !== currentUrl){
@@ -2173,7 +2180,7 @@ define([
* send logout request
* @param params
*/
let logout = function(params){
let logout = (params) => {
let data = {};
if(
params &&
@@ -2197,6 +2204,56 @@ define([
});
};
/**
* set a cookie
* @param name
* @param value
* @param expire
* @param format
*/
let setCookie = (name, value, expire, format) => {
let d = new Date();
let time = d.getTime();
let timeExpire = time * -1;
if(expire > 0){
switch(format){
case 'd': // days
timeExpire = expire * 24 * 60 * 60 * 1000; break;
case 's': // seconds
timeExpire = expire * 1000; break;
}
}
d.setTime(time + timeExpire);
let expires = 'expires=' + d.toUTCString();
let path = 'path=' + getDocumentPath();
document.cookie = name + '=' + value + '; ' + expires + '; ' + path;
};
/**
* get cookie value by name
* @param cname
* @returns {string}
*/
let getCookie = (cname) => {
let name = cname + '=';
let ca = document.cookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length,c.length);
}
}
return '';
};
return {
config: config,
getVersion: getVersion,
@@ -2239,6 +2296,7 @@ define([
deleteCurrentMapData: deleteCurrentMapData,
setCurrentUserData: setCurrentUserData,
getCurrentUserData: getCurrentUserData,
getCurrentCharacterId: getCurrentCharacterId,
setCurrentSystemData: setCurrentSystemData,
getCurrentSystemData: getCurrentSystemData,
getCurrentLocationData: getCurrentLocationData,
@@ -2253,12 +2311,14 @@ define([
getOpenDialogs: getOpenDialogs,
openIngameWindow: openIngameWindow,
formatPrice: formatPrice,
formatMassValue: formatMassValue,
getLocalStorage: getLocalStorage,
clearSessionStorage: clearSessionStorage,
getBrowserTabId: getBrowserTabId,
getObjVal: getObjVal,
getDocumentPath: getDocumentPath,
redirect: redirect,
logout: logout
logout: logout,
setCookie: setCookie,
getCookie: getCookie
};
});

8
js/lib/bootstrap-toggle.min.js vendored Normal file
View File

@@ -0,0 +1,8 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);

View File

@@ -1,8 +0,0 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap2-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-large":"small"===this.options.size?"btn-small":"mini"===this.options.size?"btn-mini":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.width(),d.width())+e.outerWidth()/2,i=this.options.height||Math.max(c.height(),d.height());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);

View File

@@ -10388,12 +10388,13 @@
// in IE the top left corner is what it placed at the desired location. This will not
// be fixed. IE8 is not going to be supported for much longer.
var ts = "translate(-50%, -50%)";
var ts = "translate(-50%, calc(-50% - .5px))";
div.style.webkitTransform = ts;
div.style.mozTransform = ts;
div.style.msTransform = ts;
div.style.oTransform = ts;
div.style.transform = ts;
div.style.WebkitFontSmoothing = 'antialiased';
// write the related component into the created element
div._jsPlumb = this;

View File

@@ -46,11 +46,11 @@ $('#jquery-drag-to-select-example').dragToSelect({
}
});
*/
jQuery.fn.dragToSelect = function (conf) {
$.fn.dragToSelect = function (conf) {
var c = typeof(conf) == 'object' ? conf : {};
// Config
var config = jQuery.extend({
var config = $.extend({
className: 'pf-map-drag-to-select',
activeClass: 'active',
disabledClass: 'disabled',
@@ -66,7 +66,7 @@ jQuery.fn.dragToSelect = function (conf) {
onRefresh: function () {return true;}
}, c);
var realParent = jQuery(this);
var realParent = $(this);
var parent = realParent;
var animationFrameId;
@@ -156,14 +156,14 @@ jQuery.fn.dragToSelect = function (conf) {
// get scroll position
var leftScroll = 0;
var rightScroll = 0;
var topScroll = 0;
if(realParent.data('scrollLeft')){
leftScroll = realParent.data('scrollLeft');
if(realParent.attr('data-scroll-left')){
leftScroll = parseInt(realParent.attr('data-scroll-left'));
}
if(realParent.data('scrollRight')){
rightScroll = realParent.data('scrollRight');
if(realParent.attr('data-scroll-top')){
topScroll = parseInt(realParent.attr('data-scroll-top'));
}
var left = lastMousePosition.x - parentDim.left + parent[0].scrollLeft;
@@ -175,13 +175,11 @@ jQuery.fn.dragToSelect = function (conf) {
newLeft = selectBoxOrigin.left - leftScroll;
var newWidth = left - selectBoxOrigin.left;
if(newWidth < 0){
newLeft = newLeft - tempWidth ;
newWidth = newWidth * -1;
}
if (top > selectBoxOrigin.top) {
newTop = selectBoxOrigin.top;
newHeight = top - selectBoxOrigin.top;
@@ -368,7 +366,7 @@ jQuery.fn.dragToSelect = function (conf) {
e.target === realParent[0] // prevent while dragging a system :)
) {
// Make sure user isn't clicking scrollbar (or disallow clicks far to the right actually)
if ((e.pageX + 20) > jQuery(document.body).width()) {
if ((e.pageX + 20) > $(document.body).width()) {
return;
}

View File

@@ -1,13 +1,13 @@
// Peity jQuery plugin version 3.2.0
// (c) 2015 Ben Pickles
// Peity jQuery plugin version 3.2.1
// (c) 2016 Ben Pickles
//
// http://benpickles.github.io/peity
//
// Released under MIT license.
(function(k,w,h,v){var d=k.fn.peity=function(a,b){y&&this.each(function(){var e=k(this),c=e.data("_peity");c?(a&&(c.type=a),k.extend(c.opts,b)):(c=new x(e,a,k.extend({},d.defaults[a],e.data("peity"),b)),e.change(function(){c.draw()}).data("_peity",c));c.draw()});return this},x=function(a,b,e){this.$el=a;this.type=b;this.opts=e},o=x.prototype,q=o.svgElement=function(a,b){return k(w.createElementNS("http://www.w3.org/2000/svg",a)).attr(b)},y="createElementNS"in w&&q("svg",{})[0].createSVGRect;o.draw=
function(){var a=this.opts;d.graphers[this.type].call(this,a);a.after&&a.after.call(this,a)};o.fill=function(){var a=this.opts.fill;return k.isFunction(a)?a:function(b,e){return a[e%a.length]}};o.prepare=function(a,b){this.$svg||this.$el.hide().after(this.$svg=q("svg",{"class":"peity"}));return this.$svg.empty().data("peity",this).attr({height:b,width:a})};o.values=function(){return k.map(this.$el.text().split(this.opts.delimiter),function(a){return parseFloat(a)})};d.defaults={};d.graphers={};d.register=
function(a,b,e){this.defaults[a]=b;this.graphers[a]=e};d.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(a){if(!a.delimiter){var b=this.$el.text().match(/[^0-9\.]/);a.delimiter=b?b[0]:","}b=k.map(this.values(),function(a){return 0<a?a:0});if("/"==a.delimiter)var e=b[0],b=[e,h.max(0,b[1]-e)];for(var c=0,e=b.length,t=0;c<e;c++)t+=b[c];t||(e=2,t=1,b=[0,1]);var l=2*a.radius,l=this.prepare(a.width||l,a.height||l),c=l.width(),f=l.height(),j=c/2,d=f/2,f=h.min(j,d),a=a.innerRadius;
"donut"==this.type&&!a&&(a=0.5*f);for(var r=h.PI,s=this.fill(),g=this.scale=function(a,b){var c=a/t*r*2-r/2;return[b*h.cos(c)+j,b*h.sin(c)+d]},m=0,c=0;c<e;c++){var u=b[c],i=u/t;if(0!=i){if(1==i)if(a)var i=j-0.01,p=d-f,n=d-a,i=q("path",{d:["M",j,p,"A",f,f,0,1,1,i,p,"L",i,n,"A",a,a,0,1,0,j,n].join(" ")});else i=q("circle",{cx:j,cy:d,r:f});else p=m+u,n=["M"].concat(g(m,f),"A",f,f,0,0.5<i?1:0,1,g(p,f),"L"),a?n=n.concat(g(p,a),"A",a,a,0,0.5<i?1:0,0,g(m,a)):n.push(j,d),m+=u,i=q("path",{d:n.join(" ")});
i.attr("fill",s.call(this,u,c,b));l.append(i)}}});d.register("donut",k.extend(!0,{},d.defaults.pie),function(a){d.graphers.pie.call(this,a)});d.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(a){var b=this.values();1==b.length&&b.push(b[0]);for(var e=h.max.apply(h,a.max==v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=a.strokeWidth,f=d.width(),j=d.height()-l,k=e-c,e=this.x=function(a){return a*
(f/(b.length-1))},r=this.y=function(a){var b=j;k&&(b-=(a-c)/k*j);return b+l/2},s=r(h.max(c,0)),g=[0,s],m=0;m<b.length;m++)g.push(e(m),r(b[m]));g.push(f,s);a.fill&&d.append(q("polygon",{fill:a.fill,points:g.join(" ")}));l&&d.append(q("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:a.stroke,"stroke-width":l,"stroke-linecap":"square"}))});d.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:0.1,width:32},function(a){for(var b=this.values(),e=h.max.apply(h,a.max==
function(){var a=this.opts;d.graphers[this.type].call(this,a);a.after&&a.after.call(this,a)};o.fill=function(){var a=this.opts.fill;return k.isFunction(a)?a:function(b,e){return a[e%a.length]}};o.prepare=function(a,b){this.$svg||this.$el.hide().after(this.$svg=q("svg",{"class":"peity"}));return this.$svg.empty().data("peity",this).attr({height:b,width:a})};o.values=function(){return k.map(this.$el.text().split(this.opts.delimiter),function(a){return parseFloat(a)})};d.defaults={};d.graphers={};d.register=
function(a,b,e){this.defaults[a]=b;this.graphers[a]=e};d.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(a){if(!a.delimiter){var b=this.$el.text().match(/[^0-9\.]/);a.delimiter=b?b[0]:","}b=k.map(this.values(),function(a){return 0<a?a:0});if("/"==a.delimiter)var e=b[0],b=[e,h.max(0,b[1]-e)];for(var c=0,e=b.length,t=0;c<e;c++)t+=b[c];t||(e=2,t=1,b=[0,1]);var l=2*a.radius,l=this.prepare(a.width||l,a.height||l),c=l.width(),f=l.height(),j=c/2,d=f/2,f=h.min(j,d),a=a.innerRadius;
"donut"==this.type&&!a&&(a=0.5*f);for(var r=h.PI,s=this.fill(),g=this.scale=function(a,b){var c=a/t*r*2-r/2;return[b*h.cos(c)+j,b*h.sin(c)+d]},m=0,c=0;c<e;c++){var u=b[c],i=u/t;if(0!=i){if(1==i)if(a)var i=j-0.01,p=d-f,n=d-a,i=q("path",{d:["M",j,p,"A",f,f,0,1,1,i,p,"L",i,n,"A",a,a,0,1,0,j,n].join(" ")});else i=q("circle",{cx:j,cy:d,r:f});else p=m+u,n=["M"].concat(g(m,f),"A",f,f,0,0.5<i?1:0,1,g(p,f),"L"),a?n=n.concat(g(p,a),"A",a,a,0,0.5<i?1:0,0,g(m,a)):n.push(j,d),m+=u,i=q("path",{d:n.join(" ")});
i.attr("fill",s.call(this,u,c,b));l.append(i)}}});d.register("donut",k.extend(!0,{},d.defaults.pie),function(a){d.graphers.pie.call(this,a)});d.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(a){var b=this.values();1==b.length&&b.push(b[0]);for(var e=h.max.apply(h,a.max==v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=a.strokeWidth,f=d.width(),j=d.height()-l,k=e-c,e=this.x=function(a){return a*
(f/(b.length-1))},r=this.y=function(a){var b=j;k&&(b-=(a-c)/k*j);return b+l/2},s=r(h.max(c,0)),g=[0,s],m=0;m<b.length;m++)g.push(e(m),r(b[m]));g.push(f,s);a.fill&&d.append(q("polygon",{fill:a.fill,points:g.join(" ")}));l&&d.append(q("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:a.stroke,"stroke-width":l,"stroke-linecap":"square"}))});d.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:0.1,width:32},function(a){for(var b=this.values(),e=h.max.apply(h,a.max==
v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=d.width(),f=d.height(),j=e-c,a=a.padding,k=this.fill(),r=this.x=function(a){return a*l/b.length},s=this.y=function(a){return f-(j?(a-c)/j*f:1)},g=0;g<b.length;g++){var m=r(g+a),u=r(g+1-a)-m,i=b[g],p=s(i),n=p,o;j?0>i?n=s(h.min(e,0)):p=s(h.max(c,0)):o=1;o=p-n;0==o&&(o=1,0<e&&j&&n--);d.append(q("rect",{fill:k.call(this,i,g,b),x:m,y:n,width:u,height:o}))}})})(jQuery,document,Math);

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More