diff --git a/.gitignore b/.gitignore index 4a64b2bd..ee44fc35 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ Temporary Items /node_modules/ /public/js/vX.X.X/ /vendor/ +/history/ diff --git a/README.md b/README.md index c07f7f55..efbfacc2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Mapping tool for [*EVE ONLINE*](https://www.eveonline.com) - Database will be cleared from time to time - Installation guide: - [wiki](https://github.com/exodus4d/pathfinder/wiki) -- Developer chat [Slack](https://slack.com) : +- Developer [Slack](https://slack.com) chat: - https://pathfinder-eve-online.slack.com - Please send me a mail for invite: pathfinder@exodus4d.de @@ -28,44 +28,46 @@ Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/ ### Project structure ``` - |-- (0755) app --> backend [*.php] - |-- app --> "Fat Free Framework" extensions - |-- lib --> "Fat Free Framework" - |-- main --> "PATHFINDER" root + |-- [0755] app/ --> backend [*.php] + |-- app/ --> "Fat Free Framework" extensions + |-- lib/ --> "Fat Free Framework" + |-- main/ --> "PATHFINDER" root |-- config.ini --> config "f3" framework |-- cron.ini --> config - cronjobs |-- environment.ini --> config - system environment |-- pathfinder.ini --> config - pathfinder |-- requirements.ini --> config - system requirements |-- routes.ini --> config - routes - |-- (0755) export --> DB export data - |-- sql --> static DB data for import (pathfinder.sql) - |-- (0755) favicon --> Favicons - |-- (0755) js --> JS source files (raw) - |-- app --> "PASTHFINDER" core files (not used for production) - |-- lib --> 3rd partie extension/library (not used for production) + |-- [0755] export/ --> static data + |-- csv/ --> *.csv used by /setup page + |-- json/ --> *.json used by /setup page + |-- sql/ --> DB dump for import (pathfinder.sql) + |-- [0755] favicon/ --> Favicons + |-- [0777] history/ --> log files (map history logs) [optional] + |-- [0755] js/ --> JS source files (raw) + |-- app/ --> "PASTHFINDER" core files (not used for production) + |-- lib/ --> 3rd partie extension/library (not used for production) |-- app.js --> require.js config (!required for production!) - |-- (0777) logs --> log files + |-- [0777] logs/ --> log files |-- ... - | -- node_modules --> node.js modules (not used for production) + | -- node_modules/ --> node.js modules (not used for production) |-- ... - |-- (0755) public --> frontend source - |-- css --> CSS dist/build folder (minified) - |-- fonts --> (icon)-Fonts - |-- img --> images - |-- js --> JS dist/build folder and source maps (minified, uglified) - |-- templates --> templates - |-- sass --> SCSS source (not used for production) - |-- ... - |-- (0777) tmp --> cache folder - |-- ... - |-- (0755) .htaccess --> reroute/caching rules ("Apache" only!) - |-- (0755) index.php + |-- [0755] public/ --> frontend source + |-- css/ --> CSS dist/build folder (minified) + |-- fonts/ --> (icon)-Fonts + |-- img/ --> images + |-- js/ --> JS dist/build folder and source maps (minified, uglified) + |-- templates/ --> templates + |-- sass/ --> SCSS source (not used for production) + |-- [0777] tmp/ --> cache folder + |-- [0755] .htaccess --> reroute/caching rules ("Apache" only!) + |-- [0755] index.php -------------------------- CI/CD config files: -------------------------- |-- .jshintrc --> "JSHint" config (not used for production) + |-- composer.json --> Composer package definition |-- config.rb --> "Compass" config (not used for production) |-- gulpfile.js --> "Gulp" task config (not used for production ) |-- package.json --> "Node.js" dependency config (not used for production) diff --git a/app/app/schema.php b/app/app/schema.php index ea6bbfd7..1aaee30f 100644 --- a/app/app/schema.php +++ b/app/app/schema.php @@ -34,23 +34,23 @@ class Schema extends Controller $this->f3->set('CACHE', false); $dbs = array( - /*'mysql' => new \DB\SQL( + 'mysql' => new \DB\SQL( 'mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', '' - ),*/ + ), 'sqlite' => new \DB\SQL( 'sqlite::memory:' - // 'sqlite:db/sqlite.db' +// 'sqlite:db/sqlite.db' ), - /*'pgsql' => new \DB\SQL( + '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' - )*/ - ); + ), +// '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'; @@ -117,10 +117,27 @@ class Schema extends Controller $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(); @@ -130,11 +147,33 @@ class Schema extends Controller $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'); + ->type($schema::DT_VARCHAR128) + ->nullable(false)->defaults('foo bar'); $table->build(); $r1 = $table->getCols(true); $this->test->expect( @@ -160,7 +199,7 @@ class Schema extends Controller // default value numeric, not nullable $table->addColumn('int_default_not_null') - ->type($schema::DT_INT4)->nullable(false)->defaults(123); + ->type($schema::DT_INT4)->nullable(false)->defaults(123); $table->build(); $r1 = $table->getCols(true); $this->test->expect( @@ -187,8 +226,8 @@ class Schema extends Controller // default value text, nullable $table->addColumn('text_default_nullable') - ->type($schema::DT_VARCHAR128) - ->defaults('foo bar'); + ->type($schema::DT_VARCHAR128) + ->defaults('foo bar'); $table->build(); $r1 = $table->getCols(true); $this->test->expect( @@ -253,9 +292,9 @@ class Schema extends Controller // current timestamp $table->addColumn('stamp') - ->type($schema::DT_TIMESTAMP) - ->nullable(false) - ->defaults($schema::DF_CURRENT_TIMESTAMP); + ->type($schema::DT_TIMESTAMP) + ->nullable(false) + ->defaults($schema::DF_CURRENT_TIMESTAMP); $table->build(); $r1 = $table->getCols(true); $this->test->expect( @@ -321,7 +360,7 @@ class Schema extends Controller $table->renameColumn('title123', 'text_default_not_null'); $table->build(); unset($result,$mapper); - + // remove column $table->dropColumn('column_1'); $table->build(); @@ -380,7 +419,7 @@ class Schema extends Controller // adding composite primary keys $table = $schema->createTable($this->tname); $table->addColumn('version')->type($schema::DT_INT4) - ->defaults(1)->nullable(false); + ->defaults(1)->nullable(false); $table->primary(array('id', 'version')); $table = $table->build(); $r1 = $table->getCols(true); @@ -399,7 +438,7 @@ class Schema extends Controller $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"); + ->type($schema::DT_VARCHAR128)->nullable(false)->defaults("foo"); $table->build(); $r1 = $table->getCols(true); $this->test->expect( @@ -461,6 +500,18 @@ class Schema extends Controller $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 @@ -520,11 +571,54 @@ class Schema extends Controller $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', + 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); @@ -538,7 +632,37 @@ class Schema extends Controller ); $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(); + + } } \ No newline at end of file diff --git a/app/config.ini b/app/config.ini index 59222d7b..94ce3573 100644 --- a/app/config.ini +++ b/app/config.ini @@ -21,6 +21,13 @@ TZ = UTC CACHE = folder=tmp/cache/ ;CACHE = redis=localhost:6379 +; Cache backend used by Session handler. +; default +; -If CACHE is enabled (see above), the same location is used for Session data (e.g. fileCache, RedisDB) +; mysql +; - Session data get stored in your 'PathfinderDB' table 'sessions' (faster) +SESSION_CACHE = mysql + ; Callback functions ============================================================================== ONERROR = Controller\Controller->showError UNLOAD = Controller\Controller->unload diff --git a/app/environment.ini b/app/environment.ini index 2eca34fd..afed2b33 100644 --- a/app/environment.ini +++ b/app/environment.ini @@ -14,13 +14,13 @@ BASE = URL = {{@SCHEME}}://local.pathfinder ; level of debug/error stack trace DEBUG = 3 -; main db -DB_DNS = mysql:host=localhost;port=3306;dbname= -DB_NAME = pathfinder -DB_USER = root -DB_PASS = +; Pathfinder database +DB_PF_DNS = mysql:host=localhost;port=3306;dbname= +DB_PF_NAME = pathfinder +DB_PF_USER = root +DB_PF_PASS = -; EVE-Online CCP Database export +; EVE-Online CCP database export DB_CCP_DNS = mysql:host=localhost;port=3306;dbname= DB_CCP_NAME = eve_citadel_min DB_CCP_USER = root @@ -60,11 +60,11 @@ BASE = URL = {{@SCHEME}}://www.pathfinder-w.space ; level of debug/error stack trace DEBUG = 0 -; main db -DB_DNS = mysql:host=localhost;port=3306;dbname= -DB_NAME = -DB_USER = -DB_PASS = +; Pathfinder database +DB_PF_DNS = mysql:host=localhost;port=3306;dbname= +DB_PF_NAME = +DB_PF_USER = +DB_PF_PASS = ; EVE-Online CCP Database export DB_CCP_DNS = mysql:host=localhost;port=3306;dbname= diff --git a/app/lib/db/cortex.php b/app/lib/db/cortex.php index c922eb34..b1fbdc7b 100644 --- a/app/lib/db/cortex.php +++ b/app/lib/db/cortex.php @@ -18,8 +18,8 @@ * https://github.com/ikkez/F3-Sugar/ * * @package DB - * @version 1.5.0-dev - * @date 27.02.2017 + * @version 1.5.0 + * @date 30.06.2017 * @since 24.04.2012 */ @@ -312,6 +312,7 @@ class Cortex extends Cursor { static public function setup($db=null, $table=null, $fields=null) { /** @var Cortex $self */ $self = get_called_class(); + $self::$schema_cache=[]; if (is_null($db) || is_null($table) || is_null($fields)) $df = $self::resolveConfiguration(); if (!is_object($db=(is_string($db=($db?:$df['db']))?\Base::instance()->get($db):$db))) @@ -836,9 +837,14 @@ class Cortex extends Cursor { array_unshift($filter,$crit); } } + if ($options) { + $options = $this->queryParser->prepareOptions($options,$this->dbsType); + if ($count) + unset($options['order']); + } return ($count) ? $this->mapper->count($filter,$options,$ttl) - : $this->mapper->find($filter,$this->queryParser->prepareOptions($options,$this->dbsType),$ttl); + : $this->mapper->find($filter,$options,$ttl); } /** @@ -1250,12 +1256,12 @@ class Cortex extends Cursor { $mmTable = $this->mmTable($relConf,$key); $filter = array($mmTable.'.'.$relConf['relField'] .' = '.$this->table.'.'.$this->primary); - $from=$mmTable; + $from = $this->db->quotekey($mmTable); if (array_key_exists($key, $this->relFilter) && !empty($this->relFilter[$key][0])) { $options=array(); - $from = $mmTable.' '.$this->_sql_left_join($key,$mmTable, - $relConf['relPK'],$relConf['relTable']); + $from = $this->db->quotekey($mmTable).' '. + $this->_sql_left_join($key,$mmTable,$relConf['relPK'],$relConf['relTable']); $relFilter = $this->relFilter[$key]; $this->_sql_mergeRelCondition($relFilter,$relConf['relTable'], $filter,$options); @@ -1266,8 +1272,8 @@ class Cortex extends Cursor { if (count($filter)>0) $this->preBinds=array_merge($this->preBinds,$filter); $this->mapper->set($alias, - '(select count('.$this->db->quotekey($mmTable.'.'.$relConf['relField']).') from '. - $this->db->quotekey($from).' where '.$crit. + '(select count('.$this->db->quotekey($mmTable.'.'.$relConf['relField']).')'. + ' from '.$from.' where '.$crit. ' group by '.$this->db->quotekey($mmTable.'.'.$relConf['relField']).')'); if ($this->whitelist && !in_array($alias,$this->whitelist)) $this->whitelist[] = $alias; @@ -2444,6 +2450,8 @@ class CortexQueryParser extends \Prefab { $child = array(); for ($i = 0, $max = count($parts); $i < $max; $i++) { $part = $parts[$i]; + if (is_string($part)) + $part = trim($part); if ($part == '(') { // add sub-bracket to parse array if ($b_offset > 0) @@ -2460,14 +2468,15 @@ class CortexQueryParser extends \Prefab { else // add sub-bracket to parse array $child[] = $part; - } // add to parse array - elseif ($b_offset > 0) - $child[] = $part; - // condition type - elseif (!is_array($part)) { - if (strtoupper(trim($part)) == 'AND') + } + elseif ($b_offset > 0) { + // add to parse array + $child[]=$part; + // condition type + } elseif (!is_array($part)) { + if (strtoupper($part) == 'AND') $add = true; - elseif (strtoupper(trim($part)) == 'OR') + elseif (strtoupper($part) == 'OR') $or = true; } else // skip $ncond[] = $part; @@ -2589,7 +2598,7 @@ class CortexQueryParser extends \Prefab { if (array_key_exists('group', $options) && is_string($options['group'])) { $keys = explode(',',$options['group']); $options['group']=array('keys'=>array(),'initial'=>array(), - 'reduce'=>'function (obj, prev) {}','finalize'=>''); + 'reduce'=>'function (obj, prev) {}','finalize'=>''); $keys = array_combine($keys,array_fill(0,count($keys),1)); $options['group']['keys']=$keys; $options['group']['initial']=$keys; diff --git a/app/lib/db/sql/schema.php b/app/lib/db/sql/schema.php index bea09c30..06ddfea8 100644 --- a/app/lib/db/sql/schema.php +++ b/app/lib/db/sql/schema.php @@ -32,77 +32,77 @@ class Schema extends DB_Utils { public $dataTypes = array( 'BOOLEAN' => array('mysql' => 'tinyint(1)', - 'sqlite2?|pgsql' => 'BOOLEAN', - 'mssql|sybase|dblib|odbc|sqlsrv' => 'bit', - 'ibm' => 'numeric(1,0)', + 'sqlite2?|pgsql' => 'BOOLEAN', + 'mssql|sybase|dblib|odbc|sqlsrv' => 'bit', + 'ibm' => 'numeric(1,0)', ), 'INT1' => array('mysql' => 'tinyint(4)', - 'sqlite2?' => 'integer(4)', - 'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint', - 'pgsql|ibm' => 'smallint', + 'sqlite2?' => 'integer(4)', + 'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint', + 'pgsql|ibm' => 'smallint', ), 'INT2' => array('mysql' => 'smallint(6)', - 'sqlite2?' => 'integer(6)', - 'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint', + 'sqlite2?' => 'integer(6)', + 'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint', ), 'INT4' => array('sqlite2?' => 'integer(11)', - 'pgsql|imb' => 'integer', - 'mysql' => 'int(11)', - 'mssql|dblib|sybase|odbc|sqlsrv' => 'int', + 'pgsql|imb' => 'integer', + 'mysql' => 'int(11)', + 'mssql|dblib|sybase|odbc|sqlsrv' => 'int', ), 'INT8' => array('sqlite2?' => 'integer(20)', - 'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint', - 'mysql' => 'bigint(20)', + 'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint', + 'mysql' => 'bigint(20)', ), 'FLOAT' => array('mysql|sqlite2?' => 'FLOAT', - 'pgsql' => 'double precision', - 'mssql|sybase|dblib|odbc|sqlsrv' => 'float', - 'imb' => 'decfloat' + 'pgsql' => 'double precision', + 'mssql|sybase|dblib|odbc|sqlsrv' => 'float', + 'imb' => 'decfloat' ), 'DOUBLE' => array('mysql|ibm' => 'decimal(18,6)', - 'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite - 'pgsql' => 'numeric(18,6)', - 'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)', + 'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite + 'pgsql' => 'numeric(18,6)', + 'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)', ), 'VARCHAR128' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)', - 'pgsql' => 'character varying(128)', + 'pgsql' => 'character varying(128)', ), 'VARCHAR256' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(255)', - 'pgsql' => 'character varying(255)', + 'pgsql' => 'character varying(255)', ), 'VARCHAR512' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(512)', - 'pgsql' => 'character varying(512)', + 'pgsql' => 'character varying(512)', ), 'TEXT' => array('mysql|sqlite2?|pgsql|mssql' => 'text', - 'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)', - 'ibm' => 'BLOB SUB_TYPE TEXT', + 'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)', + 'ibm' => 'BLOB SUB_TYPE TEXT', ), 'LONGTEXT' => array('mysql' => 'LONGTEXT', - 'sqlite2?|pgsql|mssql' => 'text', - 'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)', - 'ibm' => 'CLOB(2000000000)', + 'sqlite2?|pgsql|mssql' => 'text', + 'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)', + 'ibm' => 'CLOB(2000000000)', ), 'DATE' => array('mysql|sqlite2?|pgsql|mssql|sybase|dblib|odbc|sqlsrv|ibm' => 'date', ), 'DATETIME' => array('pgsql' => 'timestamp without time zone', - 'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime', - 'ibm' => 'timestamp', + 'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime', + 'ibm' => 'timestamp', ), 'TIMESTAMP' => array('mysql|ibm' => 'timestamp', - 'pgsql|odbc' => 'timestamp without time zone', - 'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME', + 'pgsql|odbc' => 'timestamp without time zone', + 'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME', ), 'BLOB' => array('mysql|odbc|sqlite2?|ibm' => 'blob', - 'pgsql' => 'bytea', - 'mssql|sybase|dblib' => 'image', - 'sqlsrv' => 'varbinary(max)', + 'pgsql' => 'bytea', + 'mssql|sybase|dblib' => 'image', + 'sqlsrv' => 'varbinary(max)', ), ), $defaultTypes = array( 'CUR_STAMP' => array('mysql' => 'CURRENT_TIMESTAMP', - 'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()', - 'pgsql' => 'LOCALTIMESTAMP(0)', - 'sqlite2?' => "(datetime('now','localtime'))", + 'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()', + 'pgsql' => 'LOCALTIMESTAMP(0)', + 'sqlite2?' => "(datetime('now','localtime'))", ), ); diff --git a/app/main/controller/accesscontroller.php b/app/main/controller/accesscontroller.php index e35cca71..1c095b5b 100644 --- a/app/main/controller/accesscontroller.php +++ b/app/main/controller/accesscontroller.php @@ -17,30 +17,30 @@ class AccessController extends Controller { /** * event handler * @param \Base $f3 - * @param array $params + * @param $params + * @return bool */ - function beforeroute(\Base $f3, $params) { - parent::beforeroute($f3, $params); + function beforeroute(\Base $f3, $params): bool { + if($return = parent::beforeroute($f3, $params)){ + // Any route/endpoint of a child class of this one, + // requires a valid logged in user! + if( !$this->isLoggedIn($f3) ){ + // no character found or login timer expired + $this->logoutCharacter(); - // Any route/endpoint of a child class of this one, - // requires a valid logged in user! - $loginCheck = $this->isLoggedIn($f3); - - if( !$loginCheck ){ - // no user found or login timer expired - $this->logout($f3); - - if( $f3->get('AJAX') ){ - // unauthorized request - $f3->status(403); - }else{ - // redirect to landing page - $f3->reroute(['login']); + if($f3->get('AJAX')){ + // unauthorized request + $f3->status(403); + }else{ + // redirect to landing page + $f3->reroute(['login']); + } + // skip route handler and afterroute() + $return = false; } - - // die() triggers unload() function - die(); } + + return $return; } /** @@ -48,7 +48,7 @@ class AccessController extends Controller { * @param \Base $f3 * @return bool */ - protected function isLoggedIn(\Base $f3){ + protected function isLoggedIn(\Base $f3): bool { $loginCheck = false; if( $character = $this->getCharacter() ){ if($this->checkLogTimer($f3, $character)){ @@ -84,7 +84,7 @@ class AccessController extends Controller { $minutes += $timeDiff->h * 60; $minutes += $timeDiff->i; - if($minutes <= $f3->get('PATHFINDER.TIMER.LOGGED')){ + if($minutes <= Config::getPathfinderData('timer.logged')){ $loginCheck = true; } } diff --git a/app/main/controller/admin.php b/app/main/controller/admin.php index 378bfbd1..20a514b9 100644 --- a/app/main/controller/admin.php +++ b/app/main/controller/admin.php @@ -32,9 +32,11 @@ class Admin extends Controller{ * event handler for all "views" * some global template variables are set in here * @param \Base $f3 + * @param $params + * @return bool */ - function beforeroute(\Base $f3, $params) { - parent::beforeroute($f3, $params); + function beforeroute(\Base $f3, $params): bool { + $return = parent::beforeroute($f3, $params); $f3->set('tplPage', 'login'); @@ -54,6 +56,8 @@ class Admin extends Controller{ // body element class $f3->set('tplBodyClass', 'pf-landing'); + + return $return; } /** @@ -291,7 +295,9 @@ class Admin extends Controller{ } foreach($corporations as $corporation){ - $data->corpMembers[$corporation->name] = $corporation->getCharacters(); + if($characters = $corporation->getCharacters()){ + $data->corpMembers[$corporation->name] = $corporation->getCharacters(); + } } // sort corporation from current user first diff --git a/app/main/controller/api/access.php b/app/main/controller/api/access.php index c323a54b..94898f54 100644 --- a/app/main/controller/api/access.php +++ b/app/main/controller/api/access.php @@ -12,16 +12,6 @@ use Model; class Access extends Controller\AccessController { - /** - * event handler - * @param \Base $f3 - * @param array $params - */ - function beforeroute(\Base $f3, $params) { - // set header for all routes - header('Content-type: application/json'); - parent::beforeroute($f3, $params); - } /** * search character/corporation or alliance by name diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/connection.php index 89717d28..85a490c6 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/connection.php @@ -12,15 +12,6 @@ use Model; class Connection extends Controller\AccessController { - /** - * @param \Base $f3 - * @param array $params - */ - function beforeroute(\Base $f3, $params) { - // set header for all routes - header('Content-type: application/json'); - parent::beforeroute($f3, $params); - } /** * save a new connection or updates an existing (drag/drop) between two systems @@ -29,7 +20,10 @@ class Connection extends Controller\AccessController { */ public function save(\Base $f3){ $postData = (array)$f3->get('POST'); - $newConnectionData = []; + + $return = (object) []; + $return->error = []; + $return->connectionData = (object) []; if( isset($postData['connectionData']) && @@ -75,27 +69,23 @@ class Connection extends Controller\AccessController { $connectionData['scope'] = 'wh'; $connectionData['type'] = ['wh_fresh']; } - $connectionData['mapId'] = $map; - // "updated" should not be set by client e.g. after manual drag&drop - unset($connectionData['updated']); - $connection->setData($connectionData); - if( $connection->isValid() ){ - $connection->save(); - - $newConnectionData = $connection->getData(); + if($connection->save($activeCharacter)){ + $return->connectionData = $connection->getData(); // broadcast map changes $this->broadcastMapData($connection->mapId); + }else{ + $return->error = $connection->getErrors(); } } } } - echo json_encode($newConnectionData); + echo json_encode($return); } /** diff --git a/app/main/controller/api/github.php b/app/main/controller/api/github.php index e9078788..18ccc093 100644 --- a/app/main/controller/api/github.php +++ b/app/main/controller/api/github.php @@ -7,7 +7,7 @@ */ namespace Controller\Api; -use Model; +use lib\Config; use Controller; @@ -43,7 +43,7 @@ class GitHub extends Controller\Controller { $releaseCount = 4; if( !$f3->exists($cacheKey) ){ - $apiPath = $this->getF3()->get('PATHFINDER.API.GIT_HUB') . '/repos/exodus4d/pathfinder/releases'; + $apiPath = Config::getPathfinderData('api.git_hub') . '/repos/exodus4d/pathfinder/releases'; // build request URL $options = $this->getRequestOptions(); diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index 37362da1..2719ff45 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -8,9 +8,11 @@ namespace Controller\Api; use Controller; +use data\file\FileHandler; use lib\Config; use lib\Socket; use Model; +use Exception; /** * Map controller @@ -23,24 +25,15 @@ class Map extends Controller\AccessController { const CACHE_KEY_INIT = 'CACHED_INIT'; const CACHE_KEY_MAP_DATA = 'CACHED.MAP_DATA.%s'; const CACHE_KEY_USER_DATA = 'CACHED.USER_DATA.%s_%s'; + const CACHE_KEY_HISTORY = 'CACHED_MAP_HISTORY_%s'; - /** - * event handler - * @param \Base $f3 - * @param array $params - */ - function beforeroute(\Base $f3, $params) { - // set header for all routes - header('Content-type: application/json'); - parent::beforeroute($f3, $params); - } /** * get map data cache key * @param Model\CharacterModel $character * @return string */ - protected function getMapDataCacheKey(Model\CharacterModel $character){ + protected function getMapDataCacheKey(Model\CharacterModel $character): string { return sprintf(self::CACHE_KEY_MAP_DATA, 'CHAR_' . $character->_id); } @@ -62,10 +55,19 @@ class Map extends Controller\AccessController { * @param int $systemId * @return string */ - protected function getUserDataCacheKey($mapId, $systemId = 0){ + protected function getUserDataCacheKey($mapId, $systemId = 0): string { return sprintf(self::CACHE_KEY_USER_DATA, 'MAP_' . $mapId, 'SYS_' . $systemId); } + /** + * get log history data cache key + * @param int $mapId + * @return string + */ + protected function getHistoryDataCacheKey(int $mapId): string { + return sprintf(self::CACHE_KEY_HISTORY, 'MAP_' . $mapId); + } + /** * Get all required static config data for program initialization * @param \Base $f3 @@ -79,10 +81,10 @@ class Map extends Controller\AccessController { $return = (object) []; $return->error = []; - // static program data ---------------------------------------------------------------------------------------- - $return->timer = $f3->get('PATHFINDER.TIMER'); + // static program data ------------------------------------------------------------------------------------ + $return->timer = Config::getPathfinderData('timer'); - // get all available map types -------------------------------------------------------------------------------- + // get all available map types ---------------------------------------------------------------------------- $mapType = Model\BasicModel::getNew('MapTypeModel'); $rows = $mapType->find('active = 1', null, $expireTimeSQL); @@ -103,7 +105,7 @@ class Map extends Controller\AccessController { } $return->mapTypes = $mapTypeData; - // get all available map scopes ------------------------------------------------------------------------------- + // get all available map scopes --------------------------------------------------------------------------- $mapScope = Model\BasicModel::getNew('MapScopeModel'); $rows = $mapScope->find('active = 1', null, $expireTimeSQL); $mapScopeData = []; @@ -116,7 +118,7 @@ class Map extends Controller\AccessController { } $return->mapScopes = $mapScopeData; - // get all available system status ---------------------------------------------------------------------------- + // get all available system status ------------------------------------------------------------------------ $systemStatus = Model\BasicModel::getNew('SystemStatusModel'); $rows = $systemStatus->find('active = 1', null, $expireTimeSQL); $systemScopeData = []; @@ -130,7 +132,7 @@ class Map extends Controller\AccessController { } $return->systemStatus = $systemScopeData; - // get all available system types ----------------------------------------------------------------------------- + // get all available system types ------------------------------------------------------------------------- $systemType = Model\BasicModel::getNew('SystemTypeModel'); $rows = $systemType->find('active = 1', null, $expireTimeSQL); $systemTypeData = []; @@ -143,7 +145,7 @@ class Map extends Controller\AccessController { } $return->systemType = $systemTypeData; - // get available connection scopes ---------------------------------------------------------------------------- + // get available connection scopes ------------------------------------------------------------------------ $connectionScope = Model\BasicModel::getNew('ConnectionScopeModel'); $rows = $connectionScope->find('active = 1', null, $expireTimeSQL); $connectionScopeData = []; @@ -157,7 +159,7 @@ class Map extends Controller\AccessController { } $return->connectionScopes = $connectionScopeData; - // get available character status ----------------------------------------------------------------------------- + // get available character status ------------------------------------------------------------------------- $characterStatus = Model\BasicModel::getNew('CharacterStatusModel'); $rows = $characterStatus->find('active = 1', null, $expireTimeSQL); $characterStatusData = []; @@ -171,21 +173,27 @@ class Map extends Controller\AccessController { } $return->characterStatus = $characterStatusData; - // route search config ---------------------------------------------------------------------------------------- + // route search config ------------------------------------------------------------------------------------ $return->routeSearch = [ - 'defaultCount' => $this->getF3()->get('PATHFINDER.ROUTE.SEARCH_DEFAULT_COUNT'), - 'maxDefaultCount' => $this->getF3()->get('PATHFINDER.ROUTE.MAX_Default_COUNT'), - 'limit' => $this->getF3()->get('PATHFINDER.ROUTE.LIMIT'), + 'defaultCount' => Config::getPathfinderData('route.search_default_count'), + 'maxDefaultCount' => Config::getPathfinderData('route.max_default_count'), + 'limit' => Config::getPathfinderData('route.limit') ]; - // get program routes ----------------------------------------------------------------------------------------- + // get program routes ------------------------------------------------------------------------------------- $return->routes = [ 'ssoLogin' => $this->getF3()->alias( 'sso', ['action' => 'requestAuthorization'] ) ]; - // get notification status ------------------------------------------------------------------------------------ - $return->notificationStatus = [ - 'rallySet' => (bool)Config::getNotificationMail('RALLY_SET') + // get third party APIs ----------------------------------------------------------------------------------- + $return->url = [ + 'ccpImageServer' => Config::getPathfinderData('api.ccp_image_server'), + 'zKillboard' => Config::getPathfinderData('api.z_killboard') + ]; + + // Slack integration status ------------------------------------------------------------------------------- + $return->slack = [ + 'status' => (bool)Config::getPathfinderData('slack.status') ]; $f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache ); @@ -195,7 +203,7 @@ class Map extends Controller\AccessController { // program mode (e.g. "maintenance") -------------------------------------------------------------------------- $return->programMode = [ - 'maintenance' => $this->getF3()->get('PATHFINDER.LOGIN.MODE_MAINTENANCE') + 'maintenance' => Config::getPathfinderData('login.mode_maintenance') ]; // get SSO error messages that should be shown immediately ---------------------------------------------------- @@ -263,17 +271,12 @@ class Map extends Controller\AccessController { isset($mapData['data']['systems']) && isset($mapData['data']['connections']) ){ - if(isset($mapData['config']['id'])){ - unset($mapData['config']['id']); - } - - $systemCount = count($mapData['data']['systems']); if( $systemCount <= $defaultConfig['max_systems']){ $map->setData($mapData['config']); $map->typeId = (int)$importData['typeId']; - $map->save(); + $map->save($activeCharacter); // new system IDs will be generated // therefore we need to temp store a mapping between IDs @@ -282,13 +285,10 @@ class Map extends Controller\AccessController { foreach($mapData['data']['systems'] as $systemData){ if(isset($systemData['id'])){ $oldId = (int)$systemData['id']; - unset($systemData['id']); $system->setData($systemData); $system->mapId = $map; - $system->createdCharacterId = $activeCharacter; - $system->updatedCharacterId = $activeCharacter; - $system->save(); + $system->save($activeCharacter); $tempSystemIdMapping[$oldId] = $system->id; $system->reset(); @@ -301,15 +301,11 @@ class Map extends Controller\AccessController { isset( $tempSystemIdMapping[$connectionData['source']] ) && isset( $tempSystemIdMapping[$connectionData['target']] ) ){ - if(isset($connectionData['id'])){ - unset($connectionData['id']); - } - $connection->setData($connectionData); $connection->mapId = $map; $connection->source = $tempSystemIdMapping[$connectionData['source']]; $connection->target = $tempSystemIdMapping[$connectionData['target']]; - $connection->save(); + $connection->save($activeCharacter); $connection->reset(); } @@ -391,150 +387,161 @@ class Map extends Controller\AccessController { $map->dry() || $map->hasAccess($activeCharacter) ){ - // new map - $map->setData($formData); - $map = $map->save(); + try{ + // new map + $map->setData($formData); + $map = $map->save($activeCharacter); - // save global map access. Depends on map "type" - if($map->isPrivate()){ + $mapDefaultConf = Config::getMapsDefaultConfig(); - // share map between characters -> set access - if(isset($formData['mapCharacters'])){ - // remove character corporation (re-add later) - $accessCharacters = array_diff($formData['mapCharacters'], [$activeCharacter->_id]); + // save global map access. Depends on map "type" + if($map->isPrivate()){ - // avoid abuse -> respect share limits - $maxShared = max($f3->get('PATHFINDER.MAP.PRIVATE.MAX_SHARED') - 1, 0); - $accessCharacters = array_slice($accessCharacters, 0, $maxShared); - - // clear map access. In case something has removed from access list - $map->clearAccess(); - - if($accessCharacters){ - /** - * @var $tempCharacter Model\CharacterModel - */ - $tempCharacter = Model\BasicModel::getNew('CharacterModel'); - - foreach($accessCharacters as $characterId){ - $tempCharacter->getById( (int)$characterId ); - - if( - !$tempCharacter->dry() && - $tempCharacter->shared == 1 // check if map shared is enabled - ){ - $map->setAccess($tempCharacter); - } - - $tempCharacter->reset(); - } - } - } - - // the current character itself should always have access - // just in case he removed himself :) - $map->setAccess($activeCharacter); - }elseif($map->isCorporation()){ - $corporation = $activeCharacter->getCorporation(); - - if($corporation){ - // the current user has to have a corporation when - // working on corporation maps! - - // share map between corporations -> set access - if(isset($formData['mapCorporations'])){ + // share map between characters -> set access + if(isset($formData['mapCharacters'])){ // remove character corporation (re-add later) - $accessCorporations = array_diff($formData['mapCorporations'], [$corporation->_id]); + $accessCharacters = array_diff($formData['mapCharacters'], [$activeCharacter->_id]); // avoid abuse -> respect share limits - $maxShared = max($f3->get('PATHFINDER.MAP.CORPORATION.MAX_SHARED') - 1, 0); - $accessCorporations = array_slice($accessCorporations, 0, $maxShared); + $maxShared = max($mapDefaultConf['private']['max_shared'] - 1, 0); + $accessCharacters = array_slice($accessCharacters, 0, $maxShared); // clear map access. In case something has removed from access list $map->clearAccess(); - if($accessCorporations){ + if($accessCharacters){ /** - * @var $tempCorporation Model\CorporationModel + * @var $tempCharacter Model\CharacterModel */ - $tempCorporation = Model\BasicModel::getNew('CorporationModel'); + $tempCharacter = Model\BasicModel::getNew('CharacterModel'); - foreach($accessCorporations as $corporationId){ - $tempCorporation->getById( (int)$corporationId ); + foreach($accessCharacters as $characterId){ + $tempCharacter->getById( (int)$characterId ); if( - !$tempCorporation->dry() && - $tempCorporation->shared == 1 // check if map shared is enabled + !$tempCharacter->dry() && + $tempCharacter->shared == 1 // check if map shared is enabled ){ - $map->setAccess($tempCorporation); + $map->setAccess($tempCharacter); } - $tempCorporation->reset(); + $tempCharacter->reset(); } } } - // the corporation of the current user should always have access - $map->setAccess($corporation); - } - }elseif($map->isAlliance()){ - $alliance = $activeCharacter->getAlliance(); + // the current character itself should always have access + // just in case he removed himself :) + $map->setAccess($activeCharacter); + }elseif($map->isCorporation()){ + $corporation = $activeCharacter->getCorporation(); - if($alliance){ - // the current user has to have a alliance when - // working on alliance maps! + if($corporation){ + // the current user has to have a corporation when + // working on corporation maps! - // share map between alliances -> set access - if(isset($formData['mapAlliances'])){ - // remove character alliance (re-add later) - $accessAlliances = array_diff($formData['mapAlliances'], [$alliance->_id]); + // share map between corporations -> set access + if(isset($formData['mapCorporations'])){ + // remove character corporation (re-add later) + $accessCorporations = array_diff($formData['mapCorporations'], [$corporation->_id]); - // avoid abuse -> respect share limits - $maxShared = max($f3->get('PATHFINDER.MAP.ALLIANCE.MAX_SHARED') - 1, 0); - $accessAlliances = array_slice($accessAlliances, 0, $maxShared); + // avoid abuse -> respect share limits + $maxShared = max($mapDefaultConf['corporation']['max_shared'] - 1, 0); + $accessCorporations = array_slice($accessCorporations, 0, $maxShared); - // clear map access. In case something has removed from access list - $map->clearAccess(); + // clear map access. In case something has removed from access list + $map->clearAccess(); - if($accessAlliances){ - /** - * @var $tempAlliance Model\AllianceModel - */ - $tempAlliance = Model\BasicModel::getNew('AllianceModel'); + if($accessCorporations){ + /** + * @var $tempCorporation Model\CorporationModel + */ + $tempCorporation = Model\BasicModel::getNew('CorporationModel'); - foreach($accessAlliances as $allianceId){ - $tempAlliance->getById( (int)$allianceId ); + foreach($accessCorporations as $corporationId){ + $tempCorporation->getById( (int)$corporationId ); - if( - !$tempAlliance->dry() && - $tempAlliance->shared == 1 // check if map shared is enabled - ){ - $map->setAccess($tempAlliance); + if( + !$tempCorporation->dry() && + $tempCorporation->shared == 1 // check if map shared is enabled + ){ + $map->setAccess($tempCorporation); + } + + $tempCorporation->reset(); } - - $tempAlliance->reset(); } } - } - // the alliance of the current user should always have access - $map->setAccess($alliance); + // the corporation of the current user should always have access + $map->setAccess($corporation); + } + }elseif($map->isAlliance()){ + $alliance = $activeCharacter->getAlliance(); + + if($alliance){ + // the current user has to have a alliance when + // working on alliance maps! + + // share map between alliances -> set access + if(isset($formData['mapAlliances'])){ + // remove character alliance (re-add later) + $accessAlliances = array_diff($formData['mapAlliances'], [$alliance->_id]); + + // avoid abuse -> respect share limits + $maxShared = max($mapDefaultConf['alliance']['max_shared'] - 1, 0); + $accessAlliances = array_slice($accessAlliances, 0, $maxShared); + + // clear map access. In case something has removed from access list + $map->clearAccess(); + + if($accessAlliances){ + /** + * @var $tempAlliance Model\AllianceModel + */ + $tempAlliance = Model\BasicModel::getNew('AllianceModel'); + + foreach($accessAlliances as $allianceId){ + $tempAlliance->getById( (int)$allianceId ); + + if( + !$tempAlliance->dry() && + $tempAlliance->shared == 1 // check if map shared is enabled + ){ + $map->setAccess($tempAlliance); + } + + $tempAlliance->reset(); + } + } + } + + // the alliance of the current user should always have access + $map->setAccess($alliance); + } } + // reload the same map model (refresh) + // this makes sure all data is up2date + $map->getById( $map->_id, 0 ); + + + $charactersData = $map->getCharactersData(); + $characterIds = array_map(function ($data){ + return $data->id; + }, $charactersData); + + // broadcast map Access -> and send map Data + $this->broadcastMapAccess($map, $characterIds); + + $return->mapData = $map->getData(); + }catch(Exception\ValidationException $e){ + $validationError = (object) []; + $validationError->type = 'error'; + $validationError->field = $e->getField(); + $validationError->message = $e->getMessage(); + $return->error[] = $validationError; } - // reload the same map model (refresh) - // this makes sure all data is up2date - $map->getById( $map->_id, 0 ); - - $charactersData = $map->getCharactersData(); - $characterIds = array_map(function ($data){ - return $data->id; - }, $charactersData); - - // broadcast map Access -> and send map Data - $this->broadcastMapAccess($map, $characterIds); - - $return->mapData = $map->getData(); }else{ // map access denied $captchaError = (object) []; @@ -559,18 +566,30 @@ class Map extends Controller\AccessController { */ public function delete(\Base $f3){ $mapData = (array)$f3->get('POST.mapData'); - $activeCharacter = $this->getCharacter(); + $mapId = (int)$mapData['id']; + $return = (object) []; + $return->deletedMapIds = []; - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapData['id']); - $map->delete( $activeCharacter, function($mapId){ - $this->broadcastMapDeleted($mapId); - }); + if($mapId){ + $activeCharacter = $this->getCharacter(); - echo json_encode([]); + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + + if($map->hasAccess($activeCharacter)){ + $map->setActive(false); + $map->save($activeCharacter); + $return->deletedMapIds[] = $mapId; + + // broadcast map delete + $this->broadcastMapDeleted($mapId); + } + } + + echo json_encode($return); } /** @@ -654,7 +673,7 @@ class Map extends Controller\AccessController { $return = (object) []; $return->error = []; - // get current map data =============================================================================== + // get current map data =================================================================================== $maps = $activeCharacter->getMaps(); // loop all submitted map data that should be saved @@ -680,13 +699,13 @@ class Map extends Controller\AccessController { count($connections) > 0 ){ - // map changes expected ======================================================================= + // map changes expected =========================================================================== // loop current user maps and check for changes foreach($maps as $map){ $mapChanged = false; - // update system data --------------------------------------------------------------------- + // update system data ------------------------------------------------------------------------- foreach($systems as $i => $systemData){ // check if current system belongs to the current map @@ -703,25 +722,25 @@ class Map extends Controller\AccessController { // system belongs to the current map if(is_object($filteredMap->systems)){ // update - unset($systemData['updated']); /** * @var $system Model\SystemModel */ $system = $filteredMap->systems->current(); $system->setData($systemData); - $system->updatedCharacterId = $activeCharacter; - $system->save(); - $mapChanged = true; - - // a system belongs to ONE map -> speed up for multiple maps - unset($systemData[$i]); + if($system->save($activeCharacter)){ + $mapChanged = true; + // one system belongs to ONE map -> speed up for multiple maps + unset($systemData[$i]); + }else{ + $return->error = array_merge($return->error, $system->getErrors()); + } } } } - // update connection data ----------------------------------------------------------------- + // update connection data --------------------------------------------------------------------- foreach($connections as $i => $connectionData){ // check if the current connection belongs to the current map @@ -738,19 +757,20 @@ class Map extends Controller\AccessController { // connection belongs to the current map if(is_object($filteredMap->connections)){ // update - unset($connectionData['updated']); /** * @var $connection Model\ConnectionModel */ $connection = $filteredMap->connections->current(); $connection->setData($connectionData); - $connection->save(); - $mapChanged = true; - - // a connection belongs to ONE map -> speed up for multiple maps - unset($connectionData[$i]); + if($connection->save($activeCharacter)){ + $mapChanged = true; + // one connection belongs to ONE map -> speed up for multiple maps + unset($connectionData[$i]); + }else{ + $return->error = array_merge($return->error, $connection->getErrors()); + } } } } @@ -767,7 +787,7 @@ class Map extends Controller\AccessController { // cache time(s) per user should be equal or less than this function is called // prevent request flooding - $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000; + $responseTTL = (int)Config::getPathfinderData('timer.update_server_map.delay') / 1000; $f3->set($cacheKey, $return, $responseTTL); } @@ -852,7 +872,7 @@ class Map extends Controller\AccessController { // cache time (seconds) should be equal or less than request trigger time // prevent request flooding - $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000; + $responseTTL = (int)Config::getPathfinderData('timer.update_server_user_data.delay') / 1000; // cache response $f3->set($cacheKey, $return, $responseTTL); @@ -1003,13 +1023,13 @@ class Map extends Controller\AccessController { break; } - // save source system ------------------------------------------------------------------------------------- + // save source system --------------------------------------------------------------------------------- if( $addSourceSystem && $sourceSystem && !$sourceExists ){ - $sourceSystem = $map->saveSystem($sourceSystem, $systemPosX, $systemPosY, $character); + $sourceSystem = $map->saveSystem($sourceSystem, $character, $systemPosX, $systemPosY); // get updated maps object if($sourceSystem){ $map = $sourceSystem->mapId; @@ -1021,13 +1041,13 @@ class Map extends Controller\AccessController { } } - // save target system ------------------------------------------------------------------------------------- + // save target system --------------------------------------------------------------------------------- if( $addTargetSystem && $targetSystem && !$targetExists ){ - $targetSystem = $map->saveSystem($targetSystem, $systemPosX, $systemPosY, $character); + $targetSystem = $map->saveSystem($targetSystem, $character, $systemPosX, $systemPosY); // get updated maps object if($targetSystem){ $map = $targetSystem->mapId; @@ -1036,7 +1056,7 @@ class Map extends Controller\AccessController { } } - // save connection ---------------------------------------------------------------------------------------- + // save connection ------------------------------------------------------------------------------------ if( $addConnection && $sourceExists && @@ -1046,7 +1066,7 @@ class Map extends Controller\AccessController { !$map->searchConnection( $sourceSystem, $targetSystem ) ){ $connection = $map->getNewConnection($sourceSystem, $targetSystem); - $connection = $map->saveConnection($connection); + $connection = $map->saveConnection($connection, $character); // get updated maps object if($connection){ $map = $connection->mapId; @@ -1097,6 +1117,53 @@ class Map extends Controller\AccessController { echo json_encode($connectionData); } + /** + * get map log data + * @param \Base $f3 + */ + public function getLogData(\Base $f3){ + $postData = (array)$f3->get('POST'); + $return = (object) []; + $return->data = []; + + // validate query parameters + $return->query = [ + 'mapId' => (int) $postData['mapId'], + 'offset' => FileHandler::validateOffset( (int)$postData['offset'] ), + 'limit' => FileHandler::validateLimit( (int)$postData['limit'] ) + ]; + + if($mapId = (int)$postData['mapId']){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\MapModel $map + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + + if($map->hasAccess($activeCharacter)){ + $cacheKey = $this->getHistoryDataCacheKey($mapId); + if($return->query['offset'] === 0){ + // check cache + $return->data = $f3->get($cacheKey); + } + + if(empty($return->data)){ + $return->data = $map->getLogData($return->query['offset'], $return->query['limit']); + if( + $return->query['offset'] === 0 && + !empty($return->data)) + { + $f3->set($cacheKey, $return->data, (int)Config::getPathfinderData('history.cache')); + } + } + } + } + + echo json_encode($return); + } + } diff --git a/app/main/controller/api/route.php b/app/main/controller/api/route.php index a6b69e59..807b0233 100644 --- a/app/main/controller/api/route.php +++ b/app/main/controller/api/route.php @@ -8,6 +8,7 @@ namespace Controller\Api; use Controller; +use lib\Config; use Model; @@ -530,7 +531,7 @@ class Route extends Controller\AccessController { $map = Model\BasicModel::getNew('MapModel'); // limit max search routes to max limit - array_splice($routesData, $f3->get('PATHFINDER.ROUTE.LIMIT')); + array_splice($routesData, Config::getPathfinderData('route.limit')); foreach($routesData as $key => $routeData){ // mapIds are optional. If mapIds is empty or not set @@ -609,7 +610,7 @@ class Route extends Controller\AccessController { $returnRoutData = $cachedData; }else{ // max search depth for search - $searchDepth = $f3->get('PATHFINDER.ROUTE.SEARCH_DEPTH'); + $searchDepth = Config::getPathfinderData('route.search_depth'); // set jump data for following route search // --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0 diff --git a/app/main/controller/api/signature.php b/app/main/controller/api/signature.php index 6a3b0e55..39b8250d 100644 --- a/app/main/controller/api/signature.php +++ b/app/main/controller/api/signature.php @@ -13,16 +13,6 @@ use Model; class Signature extends Controller\AccessController { - /** - * event handler - * @param \Base $f3 - * @param array $params - */ - function beforeroute(\Base $f3, $params) { - // set header for all routes - header('Content-type: application/json'); - parent::beforeroute($f3, $params); - } /** * get signature data for systems @@ -120,8 +110,6 @@ class Signature extends Controller\AccessController { if($signature->dry()){ // new signature $signature->systemId = $system; - $signature->updatedCharacterId = $activeCharacter; - $signature->createdCharacterId = $activeCharacter; $signature->setData($data); }else{ // update signature @@ -181,13 +169,11 @@ class Signature extends Controller\AccessController { } if( $signature->hasChanged($newData) ){ - // Character should only be changed if something else has changed - $signature->updatedCharacterId = $activeCharacter; $signature->setData($newData); } } - $signature->save(); + $signature->save($activeCharacter); $updatedSignatureIds[] = $signature->id; // get a fresh signature object with the new data. This is a bad work around! diff --git a/app/main/controller/api/statistic.php b/app/main/controller/api/statistic.php index ab89776e..e42dc68c 100644 --- a/app/main/controller/api/statistic.php +++ b/app/main/controller/api/statistic.php @@ -9,6 +9,7 @@ namespace controller\api; use Controller; +use lib\Config; use Model\CharacterModel; class Statistic extends Controller\AccessController { @@ -131,19 +132,19 @@ class Statistic extends Controller\AccessController { $objectId = 0; // add map-"typeId" (private/corp/ally) condition ------------------------------------------------------------- - // check if "ACTIVITY_LOGGING" is active for a given "typeId" + // check if "LOG_ACTIVITY_ENABLED" is active for a given "typeId" $sqlMapType = ""; switch($typeId){ case 2: - if( $this->getF3()->get('PATHFINDER.MAP.PRIVATE.ACTIVITY_LOGGING') ){ + if( Config::getMapsDefaultConfig('private')['log_activity_enabled'] ){ $sqlMapType .= " AND `character`.`id` = :objectId "; $objectId = $character->_id; } break; case 3: if( - $this->getF3()->get('PATHFINDER.MAP.CORPORATION.ACTIVITY_LOGGING') && + Config::getMapsDefaultConfig('corporation')['log_activity_enabled'] && $character->hasCorporation() ){ $sqlMapType .= " AND `character`.`corporationId` = :objectId "; @@ -152,7 +153,7 @@ class Statistic extends Controller\AccessController { break; case 4: if( - $this->getF3()->get('PATHFINDER.MAP.ALLIANCE.ACTIVITY_LOGGING') && + Config::getMapsDefaultConfig('alliance')['log_activity_enabled'] && $character->hasAlliance() ){ $sqlMapType .= " AND `character`.`allianceId` = :objectId "; @@ -181,6 +182,9 @@ class Statistic extends Controller\AccessController { `log`.`characterId`, `character`.`name`, `character`.`lastLogin`, + SUM(`log`.`mapCreate`) `mapCreate`, + SUM(`log`.`mapUpdate`) `mapUpdate`, + SUM(`log`.`mapDelete`) `mapDelete`, SUM(`log`.`systemCreate`) `systemCreate`, SUM(`log`.`systemUpdate`) `systemUpdate`, SUM(`log`.`systemDelete`) `systemDelete`, diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index 6fac7a27..be00945b 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -8,8 +8,8 @@ namespace Controller\Api; use Controller; -use Controller\Ccp\Sso; use Data\Mapper as Mapper; +use lib\Config; use Model; class System extends Controller\AccessController { @@ -65,16 +65,6 @@ class System extends Controller\AccessController { private $limitQuery = ""; - /** - * @param \Base $f3 - * @param array $params - */ - function beforeroute(\Base $f3, $params) { - parent::beforeroute($f3, $params); - - // set header for all routes - header('Content-type: application/json'); - } /** * build query @@ -187,9 +177,12 @@ class System extends Controller\AccessController { * @param \Base $f3 */ public function save(\Base $f3){ - $newSystemData = []; $postData = (array)$f3->get('POST'); + $return = (object) []; + $return->error = []; + $return->systemData = (object) []; + if( isset($postData['systemData']) && isset($postData['mapData']) @@ -229,23 +222,21 @@ class System extends Controller\AccessController { */ $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapData['id']); - if( - !$map->dry() && - $map->hasAccess($activeCharacter) - ){ + if( $map->hasAccess($activeCharacter) ){ // make sure system is not already on map // --> (e.g. multiple simultaneously save() calls for the same system) $systemModel = $map->getSystemByCCPId($systemData['systemId']); if( is_null($systemModel) ){ // system not found on map -> get static system data (CCP DB) $systemModel = $map->getNewSystem($systemData['systemId']); - $systemModel->createdCharacterId = $activeCharacter; - $systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : 1; + $defaultStatusId = 1; }else{ // system already exists (e.g. was inactive) - $systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $systemModel->statusId; + $defaultStatusId = $systemModel->statusId; } + $systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $defaultStatusId; + // map is not changeable for a system! (security) $systemData['mapId'] = $map; } @@ -255,28 +246,32 @@ class System extends Controller\AccessController { // "statusId" was set above unset($systemData['statusId']); unset($systemData['mapId']); - unset($systemData['createdCharacterId']); - unset($systemData['updatedCharacterId']); // set/update system $systemModel->setData($systemData); // activate system (e.g. was inactive)) $systemModel->setActive(true); - $systemModel->updatedCharacterId = $activeCharacter; - $systemModel->save(); - // get data from "fresh" model (e.g. some relational data has changed: "statusId") - $newSystemModel = Model\BasicModel::getNew('SystemModel'); - $newSystemModel->getById( $systemModel->id, 0); - $newSystemModel->clearCacheData(); - $newSystemData = $newSystemModel->getData(); - // broadcast map changes - $this->broadcastMapData($newSystemModel->mapId); + if($systemModel->save($activeCharacter)){ + // get data from "fresh" model (e.g. some relational data has changed: "statusId") + /** + * @var $newSystemModel Model\SystemModel + */ + $newSystemModel = Model\BasicModel::getNew('SystemModel'); + $newSystemModel->getById( $systemModel->id, 0); + $newSystemModel->clearCacheData(); + $return->systemData = $newSystemModel->getData(); + + // broadcast map changes + $this->broadcastMapData($newSystemModel->mapId); + }else{ + $return->error = $systemModel->getErrors(); + } } } - echo json_encode($newSystemData); + echo json_encode($return); } /** @@ -358,7 +353,7 @@ class System extends Controller\AccessController { $return->systemData[] = $systemModel->getData(); } - $f3->set($cacheKey, $return->systemData, $f3->get('PATHFINDER.CACHE.CONSTELLATION_SYSTEMS') ); + $f3->set($cacheKey, $return->systemData, Config::getPathfinderData('cache.constellation_systems')); } } @@ -407,6 +402,37 @@ class System extends Controller\AccessController { echo json_encode($return); } + /** + * send Rally Point poke + * @param \Base $f3 + */ + public function pokeRally(\Base $f3){ + $rallyData = (array)$f3->get('POST'); + $systemId = (int)$rallyData['systemId']; + $return = (object) []; + + if($systemId){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\SystemModel $system + */ + $system = Model\BasicModel::getNew('SystemModel'); + $system->getById($systemId); + + if($system->hasAccess($activeCharacter)){ + $rallyData['pokeDesktop'] = $rallyData['pokeDesktop'] === '1'; + $rallyData['pokeMail'] = $rallyData['pokeMail'] === '1'; + $rallyData['pokeSlack'] = $rallyData['pokeSlack'] === '1'; + $rallyData['message'] = trim($rallyData['message']); + + $system->sendRallyPoke($rallyData, $activeCharacter); + } + } + + echo json_encode($return); + } + /** * delete systems and all its connections from map * -> set "active" flag @@ -428,7 +454,7 @@ class System extends Controller\AccessController { $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapId); - if( $map->hasAccess($activeCharacter) ){ + if($map->hasAccess($activeCharacter)){ foreach($systemIds as $systemId){ if( $system = $map->getSystemById($systemId) ){ // check whether system should be deleted OR set "inactive" @@ -437,7 +463,7 @@ class System extends Controller\AccessController { }else{ // keep data -> set "inactive" $system->setActive(false); - $system->save(); + $system->save($activeCharacter); } $system->reset(); diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index d268af9a..7b33b808 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -8,7 +8,6 @@ namespace Controller\Api; use Controller; -use controller\MailController; use Model; use Exception; @@ -26,8 +25,8 @@ class User extends Controller\Controller{ // character specific session keys const SESSION_KEY_CHARACTERS = 'SESSION.CHARACTERS'; - // temp login character ID (during HTTP redirects on login) - const SESSION_KEY_TEMP_CHARACTER_ID = 'SESSION.TEMP_CHARACTER_ID'; + // temp login character data (during HTTP redirects on login) + const SESSION_KEY_TEMP_CHARACTER_DATA = 'SESSION.TEMP_CHARACTER_DATA'; // log text const LOG_LOGGED_IN = 'userId: [%10s], userName: [%30s], charId: [%20s], charName: %s'; @@ -43,9 +42,10 @@ class User extends Controller\Controller{ /** * login a valid character * @param Model\CharacterModel $characterModel + * @param string $browserTabId * @return bool */ - protected function loginByCharacter(Model\CharacterModel &$characterModel){ + protected function loginByCharacter(Model\CharacterModel &$characterModel, string $browserTabId){ $login = false; if($user = $characterModel->getUser()){ @@ -68,7 +68,7 @@ class User extends Controller\Controller{ ){ // user has changed OR new user --------------------------------------------------- //-> set user/character data to session - $this->f3->set(self::SESSION_KEY_USER, [ + $this->getF3()->set(self::SESSION_KEY_USER, [ 'ID' => $user->_id, 'NAME' => $user->name ]); @@ -77,7 +77,7 @@ class User extends Controller\Controller{ $sessionCharacters = $characterModel::mergeSessionCharacterData($sessionCharacters); } - $this->f3->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters); + $this->getF3()->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters); // save user login information -------------------------------------------------------- $characterModel->roleId = $characterModel->requestRoleId(); @@ -94,6 +94,10 @@ class User extends Controller\Controller{ ) ); + // set temp character data ------------------------------------------------------------ + // -> pass character data over for next http request (reroute()) + $this->setTempCharacterData($characterModel->_id, $browserTabId); + $login = true; } @@ -118,6 +122,14 @@ class User extends Controller\Controller{ if( !empty($characters = $this->getCookieCharacters(array_slice($cookieData, 0, 1, true), false)) ){ // character is valid and allowed to login $return->character = reset($characters)->getData(); + // get Session status for character + if($activeCharacter = $this->getCharacter()){ + if($activeUser = $activeCharacter->getUser()){ + if($sessionCharacterData = $activeUser->findSessionCharacterData($return->character->id)){ + $return->character->hasActiveSession = true; + } + } + } }else{ $characterError = (object) []; $characterError->type = 'warning'; @@ -179,9 +191,7 @@ class User extends Controller\Controller{ */ public function deleteLog(\Base $f3){ if($activeCharacter = $this->getCharacter()){ - if($characterLog = $activeCharacter->getLog()){ - $characterLog->erase(); - } + $activeCharacter->logout(false, true, false); } } @@ -190,8 +200,7 @@ class User extends Controller\Controller{ * @param \Base $f3 */ public function logout(\Base $f3){ - $this->deleteLog($f3); - parent::logout($f3); + $this->logoutCharacter(false, true, true, true); $return = (object) []; $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); @@ -371,23 +380,15 @@ class User extends Controller\Controller{ $user = $activeCharacter->getUser(); if($user){ - // try to send delete account mail - $msg = 'Hello ' . $user->name . ',

'; - $msg .= 'your account data has been successfully deleted.'; - - $mailController = new MailController(); - $mailController->sendDeleteAccount($user->email, $msg); - // save log self::getLogger('DELETE_ACCOUNT')->write( sprintf(self::LOG_DELETE_ACCOUNT, $user->id, $user->name) ); - // remove user + $this->logoutCharacter(true, true, true, true); $user->erase(); - $this->logout($f3); - die(); + $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); } }else{ // captcha not valid -> return error diff --git a/app/main/controller/appcontroller.php b/app/main/controller/appcontroller.php index 85777142..d730a82e 100644 --- a/app/main/controller/appcontroller.php +++ b/app/main/controller/appcontroller.php @@ -13,6 +13,33 @@ use lib\Config; class AppController extends Controller { + public function beforeroute(\Base $f3, $params) : bool{ + // page title + $f3->set('tplPageTitle', Config::getPathfinderData('name')); + + // main page content + $f3->set('tplPageContent', Config::getPathfinderData('view.login')); + + // body element class + $f3->set('tplBodyClass', 'pf-landing'); + + // JS main file + $f3->set('tplJsView', 'login'); + + if($return = parent::beforeroute($f3, $params)){ + // href for SSO Auth + $f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAuthorization'] )); + + // characters from cookies + $f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true)); + $f3->set('getCharacterGrid', function($characters){ + return ( ((12 / count($characters)) <= 3) ? 3 : (12 / count($characters)) ); + }); + } + + return $return; + } + /** * event handler after routing * @param \Base $f3 @@ -31,26 +58,7 @@ class AppController extends Controller { * @param \Base $f3 */ public function init(\Base $f3) { - // page title - $f3->set('tplPageTitle', Config::getPathfinderData('name')); - // main page content - $f3->set('tplPageContent', Config::getPathfinderData('view.login')); - - // body element class - $f3->set('tplBodyClass', 'pf-landing'); - - // JS main file - $f3->set('tplJsView', 'login'); - - // href for SSO Auth - $f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAuthorization'] )); - - // characters from cookies - $f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true)); - $f3->set('getCharacterGrid', function($characters){ - return ( ((12 / count($characters)) <= 3) ? 3 : (12 / count($characters)) ); - }); } } \ No newline at end of file diff --git a/app/main/controller/ccp/sso.php b/app/main/controller/ccp/sso.php index 6e7050d8..5e509e6c 100644 --- a/app/main/controller/ccp/sso.php +++ b/app/main/controller/ccp/sso.php @@ -34,6 +34,7 @@ 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'; @@ -53,6 +54,8 @@ class Sso extends Api\User{ * @param \Base $f3 */ 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,6 +69,10 @@ class Sso extends Api\User{ */ 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']) && @@ -73,7 +80,7 @@ class Sso extends Api\User{ ){ // authentication restricted to a characterId ----------------------------------------------- // restrict login to this characterId e.g. for character switch on map page - $characterId = (int)trim($params['characterId']); + $characterId = (int)trim((string)$params['characterId']); /** * @var Model\CharacterModel $character @@ -101,15 +108,12 @@ class Sso extends Api\User{ $character->hasUserCharacter() && ($character->isAuthorized() === 'OK') ){ - $loginCheck = $this->loginByCharacter($character); + $loginCheck = $this->loginByCharacter($character, $browserTabId); if($loginCheck){ // set "login" cookie $this->setLoginCookie($character); - // -> pass current character data to target page - $f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id); - // route to "map" $f3->reroute(['map']); } @@ -174,6 +178,8 @@ 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( @@ -186,6 +192,7 @@ 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']); @@ -252,14 +259,14 @@ class Sso extends Api\User{ $characterModel = $userCharactersModel->getCharacter(); // login by character - $loginCheck = $this->loginByCharacter($characterModel); + $loginCheck = $this->loginByCharacter($characterModel, $browserTabId); if($loginCheck){ // set "login" cookie $this->setLoginCookie($characterModel); // -> pass current character data to target page - $f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $characterModel->_id); + $f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $characterModel->_id); // route to "map" if($rootAlias == 'admin'){ @@ -302,6 +309,7 @@ class Sso extends Api\User{ 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) ){ @@ -316,12 +324,8 @@ class Sso extends Api\User{ if( is_object($character)){ // login by character - $loginCheck = $this->loginByCharacter($character); + $loginCheck = $this->loginByCharacter($character, $browserTabId); if($loginCheck){ - // set character id - // -> pass current character data to target page - $f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id); - // route to "map" $f3->reroute(['map']); } diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index 44d636aa..cf305d58 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -9,6 +9,7 @@ namespace Controller; use Controller\Api as Api; use lib\Config; +use Lib\Monolog; use lib\Socket; use Lib\Util; use Model; @@ -21,8 +22,8 @@ class Controller { const COOKIE_PREFIX_CHARACTER = 'char'; // log text - const LOG_UNAUTHORIZED = 'User-Agent: [%s]'; - const ERROR_SESSION_SUSPECT = 'Suspect id: [%45s], ip: [%45s], new ip: [%45s], User-Agent: [%s]'; + const ERROR_SESSION_SUSPECT = 'id: [%45s], ip: [%45s], User-Agent: [%s]'; + const ERROR_TEMP_CHARACTER_ID = 'Invalid temp characterId: %s'; /** * @var \Base @@ -48,46 +49,38 @@ class Controller { return $this->template; } - /** - * set $f3 base object - * @param \Base $f3 - */ - protected function setF3(\Base $f3){ - $this->f3 = $f3; - } - /** * get $f3 base object * @return \Base */ protected function getF3(){ - if( !($this->f3 instanceof \Base) ){ - $this->setF3( \Base::instance() ); - } - return $this->f3; + return \Base::instance(); } /** * event handler for all "views" * some global template variables are set in here * @param \Base $f3 - * @param array $params + * @param $params + * @return bool */ - function beforeroute(\Base $f3, $params) { - $this->setF3($f3); - + function beforeroute(\Base $f3, $params): bool { // initiate DB connection - DB\Database::instance('PF'); + DB\Database::instance()->getDB('PF'); // init user session - $this->initSession(); + $this->initSession($f3); - if( !$f3->get('AJAX') ){ + if($f3->get('AJAX')){ + header('Content-type: application/json'); + }else{ // js path (build/minified or raw uncompressed files) $f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') ); $this->setTemplate( Config::getPathfinderData('view.index') ); } + + return true; } /** @@ -96,9 +89,6 @@ class Controller { * @param \Base $f3 */ public function afterroute(\Base $f3){ - // store all user activities that are buffered for logging in this request - self::storeActivities(); - if($this->getTemplate()){ // Ajax calls don´t need a page render.. // this happens on client side @@ -118,32 +108,35 @@ class Controller { /** * init new Session handler */ - protected function initSession(){ + protected function initSession(\Base $f3){ + $sessionCacheKey = $f3->get('SESSION_CACHE'); + $session = null; - // init DB based Session (not file based) - if( $this->getDB('PF') instanceof DB\SQL){ - // init session with custom "onsuspect()" handler - new DB\SQL\Session($this->getDB('PF'), 'sessions', true, function($session, $sid){ - $f3 = $this->getF3(); - if( ($ip = $session->ip() )!= $f3->get('IP') ){ - // IP address changed -> not critical - self::getLogger('SESSION_SUSPECT')->write( sprintf( - self::ERROR_SESSION_SUSPECT, - $sid, - $session->ip(), - $f3->get('IP'), - $f3->get('AGENT') - )); - // no more error handling here - return true; - }elseif($session->agent() != $f3->get('AGENT') ){ - // The default behaviour destroys the suspicious session. - return false; - } + /** + * callback() for suspect sessions + * @param $session + * @param $sid + * @return bool + */ + $onSuspect = function($session, $sid){ + self::getLogger('SESSION_SUSPECT')->write( sprintf( + self::ERROR_SESSION_SUSPECT, + $sid, + $session->ip(), + $session->agent() + )); + // .. continue with default onSuspect() handler + // -> destroy session + return false; + }; - return true; - }); + if( + $sessionCacheKey === 'mysql' && + $this->getDB('PF') instanceof DB\SQL + ){ + $session = new DB\SQL\Session($this->getDB('PF'), 'sessions', true, $onSuspect); } + } /** @@ -190,12 +183,11 @@ class Controller { * @param Model\CharacterModel $character */ protected function setLoginCookie(Model\CharacterModel $character){ - if( $this->getCookieState() ){ - $expireSeconds = (int) $this->getF3()->get('PATHFINDER.LOGIN.COOKIE_EXPIRE'); + $expireSeconds = (int)Config::getPathfinderData('login.cookie_expire'); $expireSeconds *= 24 * 60 * 60; - $timezone = new \DateTimeZone( $this->getF3()->get('TZ') ); + $timezone = $this->getF3()->get('getTimeZone')(); $expireTime = new \DateTime('now', $timezone); // add cookie expire time @@ -258,7 +250,7 @@ class Controller { */ $characterAuth = Model\BasicModel::getNew('CharacterAuthenticationModel'); - $timezone = new \DateTimeZone( $this->getF3()->get('TZ') ); + $timezone = $this->getF3()->get('getTimeZone')(); $currentTime = new \DateTime('now', $timezone); foreach($cookieData as $name => $value){ @@ -268,7 +260,7 @@ class Controller { $data = explode(':', $value); if(count($data) === 2){ // cookie data is well formatted - $characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1], 0); + $characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1]); // validate "scope hash" // -> either "normal" scopes OR "admin" scopes @@ -354,28 +346,37 @@ class Controller { $data = []; if($user = $this->getUser()){ - $requestedCharacterId = 0; + $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); - // get all characterData from currently active characters if($this->getF3()->get('AJAX')){ - // Ajax request -> get characterId from Header (if already available!) - $header = $this->getRequestHeaders(); - $requestedCharacterId = (int)$header['Pf-Character']; + + // _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( - $requestedCharacterId > 0 && - (int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID) === $requestedCharacterId + !empty($browserTabId) && + !empty($tempCharacterData['TAB_ID']) && + (int)$tempCharacterData['ID'] > 0 && + $browserTabId === $tempCharacterData['TAB_ID'] ){ - // requested characterId is "now" available on the client (Javascript) - // -> clear temp characterId for next character login/switch - $this->getF3()->clear(Api\User::SESSION_KEY_TEMP_CHARACTER_ID); + $requestedCharacterId = (int)$tempCharacterData['ID']; } - } - if($requestedCharacterId <= 0){ - // Ajax BUT characterID not yet set as HTTP header - // OR non Ajax -> get characterId from temp session (e.g. from HTTP redirect) - $requestedCharacterId = (int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID); + }elseif((int)$tempCharacterData['ID'] > 0){ + $requestedCharacterId = (int)$tempCharacterData['ID']; } $data = $user->getSessionCharacterData($requestedCharacterId); @@ -420,21 +421,18 @@ class Controller { public function getUser($ttl = 0){ $user = null; - if( $this->getF3()->exists(Api\User::SESSION_KEY_USER_ID) ){ - $userId = (int)$this->getF3()->get(Api\User::SESSION_KEY_USER_ID); - if($userId){ - /** - * @var $userModel Model\UserModel - */ - $userModel = Model\BasicModel::getNew('UserModel'); - $userModel->getById($userId, $ttl); + if($this->getF3()->exists(Api\User::SESSION_KEY_USER_ID, $userId)){ + /** + * @var $userModel Model\UserModel + */ + $userModel = Model\BasicModel::getNew('UserModel'); + $userModel->getById($userId, $ttl); - if( - !$userModel->dry() && - $userModel->hasUserCharacters() - ){ - $user = &$userModel; - } + if( + !$userModel->dry() && + $userModel->hasUserCharacters() + ){ + $user = &$userModel; } } @@ -442,26 +440,57 @@ class Controller { } /** - * log out current character - * @param \Base $f3 + * set temp login character data (required during HTTP redirects on login) + * @param int $characterId + * @param string $browserTabId + * @throws \Exception */ - public function logout(\Base $f3){ - $params = (array)$f3->get('POST'); + protected function setTempCharacterData(int $characterId, string $browserTabId){ + if($characterId > 0){ + $tempCharacterData = [ + 'ID' => $characterId, + 'TAB_ID' => trim($browserTabId) + ]; + $this->getF3()->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $tempCharacterData); + }else{ + throw new \Exception( sprintf(self::ERROR_TEMP_CHARACTER_ID, $characterId) ); + } + } - if( $activeCharacter = $this->getCharacter() ){ + /** + * log out current character or all active characters (multiple browser tabs) + * @param bool $all + * @param bool $deleteSession + * @param bool $deleteLog + * @param bool $deleteCookie + */ + 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); - if($params['clearCookies'] === '1'){ - // delete server side cookie validation data - // for the active character - $activeCharacter->logout(); + if($sessionCharacterData){ + $activeCharacterId = ($activeCharacter = $this->getCharacter()) ? $activeCharacter->_id : 0; + /** + * @var Model\CharacterModel $character + */ + $character = Model\BasicModel::getNew('CharacterModel'); + $characterIds = []; + foreach($sessionCharacterData as $characterData){ + if($characterData['ID'] === $activeCharacterId){ + $characterIds[] = $activeCharacter->_id; + $activeCharacter->logout($deleteSession, $deleteLog, $deleteCookie); + }elseif($all){ + $character->getById($characterData['ID']); + $characterIds[] = $character->_id; + $character->logout($deleteSession, $deleteLog, $deleteCookie); + } + $character->reset(); } - // broadcast logout information to webSocket server - (new Socket( Config::getSocketUri() ))->sendData('characterLogout', $activeCharacter->_id); + if($characterIds){ + // broadcast logout information to webSocket server + (new Socket( Config::getSocketUri() ))->sendData('characterLogout', $characterIds); + } } - - // destroy session login data ------------------------------- - $f3->clear('SESSION'); } /** @@ -482,7 +511,7 @@ class Controller { if( !empty($response) ){ // calculate time diff since last server restart - $timezone = new \DateTimeZone( $f3->get('TZ') ); + $timezone = $f3->get('getTimeZone')(); $dateNow = new \DateTime('now', $timezone); $dateServerStart = new \DateTime($response['startTime']); $interval = $dateNow->diff($dateServerStart); @@ -504,14 +533,24 @@ class Controller { } /** - * get error object is a user is not found/logged of + * @param int $code + * @param string $message + * @param string $status + * @param null $trace * @return \stdClass */ - protected function getLogoutError(){ - $userError = (object) []; - $userError->type = 'error'; - $userError->message = 'User not found'; - return $userError; + protected function getErrorObject(int $code, string $message = '', string $status = '', $trace = null): \stdClass{ + $object = (object) []; + $object->type = 'error'; + $object->code = $code; + $object->status = empty($status) ? @constant('Base::HTTP_' . $code) : $status; + if(!empty($message)){ + $object->message = $message; + } + if(!empty($trace)){ + $object->trace = $trace; + } + return $object; } /** @@ -558,59 +597,53 @@ class Controller { * -> on AJAX request -> return JSON with error information * -> on HTTP request -> render error page * @param \Base $f3 + * @return bool */ public function showError(\Base $f3){ - // set HTTP status - $errorCode = $f3->get('ERROR.code'); - if(!empty($errorCode)){ - $f3->status($errorCode); - } + if(!headers_sent()){ + // collect error info ------------------------------------------------------------------------------------- + $error = $this->getErrorObject( + $f3->get('ERROR.code'), + $f3->get('ERROR.status'), + $f3->get('ERROR.text'), + $f3->get('DEBUG') === 3 ? $f3->get('ERROR.trace') : null + ); - // collect error info --------------------------------------- - $return = (object) []; - $error = (object) []; - $error->type = 'error'; - $error->code = $errorCode; - $error->status = $f3->get('ERROR.status'); - $error->message = $f3->get('ERROR.text'); + // check if error is a PDO Exception ---------------------------------------------------------------------- + if(strpos(strtolower( $f3->get('ERROR.text') ), 'duplicate') !== false){ + preg_match_all('/\'([^\']+)\'/', $f3->get('ERROR.text'), $matches, PREG_SET_ORDER); - // append stack trace for greater debug level - if( $f3->get('DEBUG') === 3){ - $error->trace = $f3->get('ERROR.trace'); - } - - // check if error is a PDO Exception - if(strpos(strtolower( $f3->get('ERROR.text') ), 'duplicate') !== false){ - preg_match_all('/\'([^\']+)\'/', $f3->get('ERROR.text'), $matches, PREG_SET_ORDER); - - if(count($matches) === 2){ - $error->field = $matches[1][1]; - $error->message = 'Value "' . $matches[0][1] . '" already exists'; - } - } - $return->error[] = $error; - - // return error information --------------------------------- - if($f3->get('AJAX')){ - header('Content-type: application/json'); - echo json_encode($return); - die(); - }else{ - $f3->set('tplPageTitle', 'ERROR - ' . $error->code . ' | Pathfinder'); - // set error data for template rendering - $error->redirectUrl = $this->getRouteUrl(); - $f3->set('errorData', $error); - - if( preg_match('/^4[0-9]{2}$/', $error->code) ){ - // 4xx error -> render error page - $f3->set('tplPageContent', Config::getPathfinderData('STATUS.4XX') ); - }elseif( preg_match('/^5[0-9]{2}$/', $error->code) ){ - $f3->set('tplPageContent', Config::getPathfinderData('STATUS.5XX')); + if(count($matches) === 2){ + $error->field = $matches[1][1]; + $error->message = 'Value "' . $matches[0][1] . '" already exists'; + } } - echo \Template::instance()->render( Config::getPathfinderData('view.index') ); - die(); + // set response status ------------------------------------------------------------------------------------ + if(!empty($error->code)){ + $f3->status($error->code); + } + + if($f3->get('AJAX')){ + $return = (object) []; + $return->error[] = $error; + echo json_encode($return); + }else{ + $f3->set('tplPageTitle', 'ERROR - ' . $error->code); + // set error data for template rendering + $error->redirectUrl = $this->getRouteUrl(); + $f3->set('errorData', $error); + + if( preg_match('/^4[0-9]{2}$/', $error->code) ){ + // 4xx error -> render error page + $f3->set('tplPageContent', Config::getPathfinderData('STATUS.4XX') ); + }elseif( preg_match('/^5[0-9]{2}$/', $error->code) ){ + $f3->set('tplPageContent', Config::getPathfinderData('STATUS.5XX')); + } + } } + + return true; } /** @@ -624,44 +657,36 @@ class Controller { // track some 4xx Client side errors // 5xx errors are handled in "ONERROR" callback $status = http_response_code(); - $halt = false; + if(!headers_sent() && $status >= 300){ + if($f3->get('AJAX')){ + $params = (array)$f3->get('POST'); + $return = (object) []; + if((bool)$params['reroute']){ + $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); + }else{ + // no reroute -> errors can be shown + $return->error[] = $this->getErrorObject($status, Config::getMessageFromHTTPStatus($status)); + } - switch( $status ){ - case 403: // Unauthorized - self::getLogger('UNAUTHORIZED')->write(sprintf( - self::LOG_UNAUTHORIZED, - $f3->get('AGENT') - )); - $halt = true; - break; - } - - // Ajax - if( - $halt && - $f3->get('AJAX') - ){ - $params = (array)$f3->get('POST'); - $response = (object) []; - $response->type = 'error'; - $response->code = $status; - $response->message = 'Access denied: User not found'; - - $return = (object) []; - if( (bool)$params['reroute']){ - $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); - }else{ - // no reroute -> errors can be shown - $return->error[] = $response; + echo json_encode($return); } - - echo json_encode($return); - die(); } + // store all user activities that are buffered for logging in this request + // this should work even on non HTTP200 responses + $this->logActivities(); + return true; } + /** + * store activity log data to DB + */ + protected function logActivities(){ + LogController::instance()->logActivities(); + Monolog::instance()->log(); + } + /** * get controller by class name * -> controller class is searched within all controller directories @@ -793,7 +818,7 @@ class Controller { * @return int */ static function getRegistrationStatus(){ - return (int)\Base::instance()->get('PATHFINDER.REGISTRATION.STATUS'); + return (int)Config::getPathfinderData('registration.status'); } /** @@ -806,13 +831,6 @@ class Controller { return LogController::getLogger($type); } - /** - * store activity log data to DB - */ - static function storeActivities(){ - LogController::instance()->storeActivities(); - } - /** * removes illegal characters from a Hive-key that are not allowed * @param $key @@ -842,20 +860,4 @@ class Controller { (new Socket( Config::getSocketUri(), $ttl ))->sendData('healthCheck', $load); } - /** - * get required MySQL variable value - * @param $key - * @return string|null - */ - static function getRequiredMySqlVariables($key){ - $f3 = \Base::instance(); - $requiredMySqlVarKey = 'REQUIREMENTS[MYSQL][VARS][' . $key . ']'; - $data = null; - - if( $f3->exists($requiredMySqlVarKey) ){ - $data = $f3->get($requiredMySqlVarKey); - } - return $data; - } - } \ No newline at end of file diff --git a/app/main/controller/logcontroller.php b/app/main/controller/logcontroller.php index c00a60af..b3819714 100644 --- a/app/main/controller/logcontroller.php +++ b/app/main/controller/logcontroller.php @@ -8,42 +8,73 @@ namespace controller; use DB; +use lib\Config; +use Lib\Logging\MapLog; +use Model\ActivityLogModel; +use Model\BasicModel; class LogController extends \Prefab { + const CACHE_KEY_ACTIVITY_COLUMNS = 'CACHED_ACTIVITY_COLUMNS'; + const CACHE_TTL_ACTIVITY_COLUMNS = 300; + + /** + * @var string[] + */ + protected $activityLogColumns = []; + /** * buffered activity log data for this singleton LogController() class * -> this buffered data can be stored somewhere (e.g. DB) before HTTP response * -> should be cleared afterwards! * @var array */ - protected $activityLogBuffer = []; + protected $activityLogBuffer = []; /** - * reserve a "new" character activity for logging - * @param $characterId - * @param $mapId - * @param $action + * get columns from ActivityLogModel that can be uses as counter + * @return array */ - public function bufferActivity($characterId, $mapId, $action){ - $characterId = (int)$characterId; - $mapId = (int)$mapId; + protected function getActivityLogColumns(): array{ + if(empty($this->activityLogColumns)){ + $f3 = \Base::instance(); + if(!$f3->exists(self::CACHE_KEY_ACTIVITY_COLUMNS, $this->activityLogColumns)){ + /** + * @var $activityLogModel ActivityLogModel + */ + $activityLogModel = BasicModel::getNew('ActivityLogModel'); + $this->activityLogColumns = $activityLogModel->getCountableColumnNames(); + $f3->set(self::CACHE_KEY_ACTIVITY_COLUMNS, self::CACHE_TTL_ACTIVITY_COLUMNS); + } + } - if( - $characterId > 0 && - $mapId > 0 - ){ - $key = $this->getBufferedActivityKey($characterId, $mapId); + return $this->activityLogColumns; + } - if( is_null($key) ){ - $activity = [ - 'characterId' => $characterId, - 'mapId' => $mapId, - $action => 1 - ]; - $this->activityLogBuffer[] = $activity; - }else{ - $this->activityLogBuffer[$key][$action]++; + /** + * buffered activity log data for this singleton LogController() class + * -> this buffered data can be stored somewhere (e.g. DB) before HTTP response + * -> should be cleared afterwards! + * @param MapLog $log + */ + public function push(MapLog $log){ + $action = $log->getAction(); + + // check $action to be valid (table column exists) + if($action && in_array($action, $this->getActivityLogColumns())){ + if($mapId = $log->getChannelId()){ + $logData = $log->getData(); + if($characterId = (int)$logData['character']['id']){ + if($index = $this->getBufferedActivityIndex($characterId, $mapId)){ + $this->activityLogBuffer[$index][$action]++; + }else{ + $this->activityLogBuffer[] = [ + 'characterId' => $characterId, + 'mapId' => $mapId, + $action => 1 + ]; + } + } } } } @@ -51,7 +82,7 @@ class LogController extends \Prefab { /** * store all buffered activity log data to DB */ - public function storeActivities(){ + public function logActivities(){ if( !empty($this->activityLogBuffer) ){ $db = DB\Database::instance()->getDB('PF'); @@ -104,24 +135,20 @@ class LogController extends \Prefab { } /** - * get array key from "buffered activity log" array + * get array key/index from "buffered activity log" array * @param int $characterId * @param int $mapId - * @return int|null + * @return int */ - private function getBufferedActivityKey($characterId, $mapId){ - $activityKey = null; - - if( - $characterId > 0 && - $mapId > 0 - ){ + private function getBufferedActivityIndex(int $characterId, int $mapId): int { + $activityKey = 0; + if($characterId > 0 && $mapId > 0 ){ foreach($this->activityLogBuffer as $key => $activityData){ if( $activityData['characterId'] === $characterId && $activityData['mapId'] === $mapId ){ - $activityKey = $key; + $activityKey = (int)$key; break; } } @@ -136,8 +163,7 @@ class LogController extends \Prefab { * @return \Log|null */ public static function getLogger($type){ - $f3 = \Base::instance(); - $logFiles = $f3->get('PATHFINDER.LOGFILES'); + $logFiles = Config::getPathfinderData('logfiles'); $logFileName = empty($logFiles[$type]) ? 'error' : $logFiles[$type]; $logFile = $logFileName . '.log'; diff --git a/app/main/controller/mailcontroller.php b/app/main/controller/mailcontroller.php deleted file mode 100644 index 2477d66c..00000000 --- a/app/main/controller/mailcontroller.php +++ /dev/null @@ -1,80 +0,0 @@ -set('Errors-to', '<' . Controller::getEnvironmentData('SMTP_ERROR') . '>'); - $this->set('MIME-Version', '1.0'); - $this->set('Content-Type', 'text/html; charset=ISO-8859-1'); - } - - /** - * send mail to removed user account - * @param $to - * @param $msg - * @return bool - */ - public function sendDeleteAccount($to, $msg){ - $status = false; - - if( !empty($to)){ - $this->set('To', '<' . $to . '>'); - $this->set('From', '"Pathfinder" <' . Controller::getEnvironmentData('SMTP_FROM') . '>'); - $this->set('Subject', 'Account deleted'); - $status = $this->send($msg); - } - - return $status; - } - - /** - * send notification mail for new rally point systems - * @param $to - * @param $msg - * @return bool - */ - public function sendRallyPoint($to, $msg){ - $status = false; - - if( !empty($to)){ - $this->set('To', '<' . $to . '>'); - $this->set('From', '"Pathfinder" <' . Controller::getEnvironmentData('SMTP_FROM') . '>'); - $this->set('Subject', 'PATHFINDER - New rally point'); - $status = $this->send($msg); - } - - return $status; - } - - public function send($message, $log = true, $mock = false){ - $status = false; - - if( - !empty($this->host) && - !empty($this->port) - ){ - $status = parent::send($message, $log, $mock); - } - - return $status; - } -} \ No newline at end of file diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php index 080c39aa..2c929f62 100644 --- a/app/main/controller/setup.php +++ b/app/main/controller/setup.php @@ -13,6 +13,7 @@ use DB; use DB\SQL; use DB\SQL\MySQL as MySQL; use lib\Config; +use Lib\Util; use Model; class Setup extends Controller { @@ -26,10 +27,10 @@ class Setup extends Controller { 'BASE', 'URL', 'DEBUG', - 'DB_DNS', - 'DB_NAME', - 'DB_USER', - 'DB_PASS', + 'DB_PF_DNS', + 'DB_PF_NAME', + 'DB_PF_USER', + 'DB_PF_PASS', 'DB_CCP_DNS', 'DB_CCP_NAME', 'DB_CCP_USER', @@ -95,15 +96,15 @@ class Setup extends Controller { ], 'tables' => [] ], - /* WIP ... 'UNIVERSE' => [ 'info' => [], 'models' => [ - 'Model\Universe\RegionModel', - 'Model\Universe\ConstellationModel' + 'Model\Universe\TypeModel', + //'Model\Universe\RegionModel', + //'Model\Universe\ConstellationModel' ], 'tables' => [] - ], */ + ], 'CCP' => [ 'info' => [], 'models' => [], @@ -120,19 +121,28 @@ class Setup extends Controller { ] ]; + /** + * @var DB\Database + */ + protected $dbLib = null; + /** * database error * @var bool */ - protected $databaseCheck = true; + protected $databaseHasError = false; /** * event handler for all "views" * some global template variables are set in here * @param \Base $f3 * @param array $params + * @return bool */ - function beforeroute(\Base $f3, $params) { + function beforeroute(\Base $f3, $params): bool { + // init dbLib class. Manages all DB connections + $this->dbLib = DB\Database::instance(); + // page title $f3->set('tplPageTitle', 'Setup | ' . Config::getPathfinderData('name')); @@ -144,8 +154,13 @@ class Setup extends Controller { // js path (build/minified or raw uncompressed files) $f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') ); + + return true; } + /** + * @param \Base $f3 + */ public function afterroute(\Base $f3) { // js view (file) $f3->set('tplJsView', 'setup'); @@ -159,6 +174,16 @@ class Setup extends Controller { return $cacheType; }); + // simple counter (called within template) + $counter = 0; + $f3->set('tplCounter', function(string $action = 'add') use (&$counter){ + switch($action){ + case 'add': $counter++; break; + case 'get': return $counter; break; + case 'reset': $counter = 0; break; + } + }); + // render view echo \Template::instance()->render( Config::getPathfinderData('view.index') ); } @@ -175,26 +200,31 @@ class Setup extends Controller { // enables automatic column fix $fixColumns = false; - // bootstrap database from model class definition - if( !empty($params['db']) ){ - $this->bootstrapDB($params['db']); - - // reload page - // -> remove GET param - $f3->reroute('@setup'); - return; - }elseif( !empty($params['fixCols']) ){ - $fixColumns = true; - }elseif( !empty($params['buildIndex']) ){ - $this->setupSystemJumpTable(); - }elseif( !empty($params['importTable']) ){ - $this->importTable($params['importTable']); - }elseif( !empty($params['exportTable']) ){ - $this->exportTable($params['exportTable']); - }elseif( !empty($params['clearCache']) ){ - $this->clearCache($f3); - }elseif( !empty($params['invalidateCookies']) ){ - $this->invalidateCookies($f3); + switch($params['action']){ + case 'createDB': + $this->createDB($params['db']); + break; + case 'bootstrapDB': + $this->bootstrapDB($params['db']); + break; + case 'fixCols': + $fixColumns = true; + break; + case 'buildIndex': + $this->setupSystemJumpTable(); + break; + case 'importTable': + $this->importTable($params['model']); + break; + case 'exportTable': + $this->exportTable($params['model']); + break; + case 'clearCache': + $this->clearCache($f3); + break; + case 'invalidateCookies': + $this->invalidateCookies($f3); + break; } // set template data ---------------------------------------------------------------- @@ -207,6 +237,12 @@ class Setup extends Controller { // set requirement check information $f3->set('checkRequirements', $this->checkRequirements($f3)); + // set php config check information + $f3->set('checkPHPConfig', $this->checkPHPConfig($f3)); + + // set map default config + $f3->set('mapsDefaultConfig', $this->getMapsDefaultConfig($f3)); + // set database connection information $f3->set('checkDatabase', $this->checkDatabase($f3, $fixColumns)); @@ -320,9 +356,9 @@ class Setup extends Controller { protected function getEnvironmentInformation(\Base $f3){ $environmentData = []; // exclude some sensitive data (e.g. database, passwords) - $excludeVars = ['DB_DNS', 'DB_NAME', 'DB_USER', - 'DB_PASS', 'DB_CCP_DNS', 'DB_CCP_NAME', - 'DB_CCP_USER', 'DB_CCP_PASS' + $excludeVars = [ + 'DB_PF_DNS', 'DB_PF_NAME', 'DB_PF_USER', 'DB_PF_PASS', + 'DB_CCP_DNS', 'DB_CCP_NAME', 'DB_CCP_USER', 'DB_CCP_PASS' ]; // obscure some values @@ -338,10 +374,7 @@ class Setup extends Controller { $check = false; $value = '[missing]'; }elseif( in_array($var, $obscureVars)){ - $length = strlen($value); - $hideChars = ($length < 10) ? $length : 10; - $value = substr_replace($value, str_repeat('.', 3), -$hideChars); - $value .= ' [' . $length . ']'; + $value = Util::obscureString($value); } $environmentData[$var] = [ @@ -444,6 +477,9 @@ class Setup extends Controller { 'version' => (PHP_INT_SIZE * 8) . '-bit', 'check' => $f3->get('REQUIREMENTS.PHP.PHP_INT_SIZE') == PHP_INT_SIZE ], + [ + 'label' => 'PHP extensions' + ], 'pcre' => [ 'label' => 'PCRE', 'required' => $f3->get('REQUIREMENTS.PHP.PCRE_VERSION'), @@ -486,20 +522,6 @@ class Setup extends Controller { 'version' => (extension_loaded('curl') && function_exists('curl_version')) ? 'installed' : 'missing', 'check' => (extension_loaded('curl') && function_exists('curl_version')) ], - 'maxInputVars' => [ - 'label' => 'max_input_vars', - 'required' => $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'), - 'version' => ini_get('max_input_vars'), - 'check' => ini_get('max_input_vars') >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'), - 'tooltip' => 'PHP default = 1000. Increase it in order to import larger maps.' - ], - 'maxExecutionTime' => [ - 'label' => 'max_execution_time', - 'required' => $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'), - 'version' => ini_get('max_execution_time'), - 'check' => ini_get('max_execution_time') >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'), - 'tooltip' => 'PHP default = 30. Max execution time for PHP scripts.' - ], [ 'label' => 'Redis Server [optional]' ], @@ -594,6 +616,125 @@ class Setup extends Controller { return $checkRequirements; } + /** + * check PHP config (php.ini) + * @param \Base $f3 + * @return array + */ + protected function checkPHPConfig(\Base $f3): array { + $phpConfig = [ + 'maxInputVars' => [ + 'label' => 'max_input_vars', + 'required' => $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'), + 'version' => ini_get('max_input_vars'), + 'check' => ini_get('max_input_vars') >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'), + 'tooltip' => 'PHP default = 1000. Increase it in order to import larger maps.' + ], + 'maxExecutionTime' => [ + 'label' => 'max_execution_time', + 'required' => $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'), + 'version' => ini_get('max_execution_time'), + 'check' => ini_get('max_execution_time') >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'), + 'tooltip' => 'PHP default = 30. Max execution time for PHP scripts.' + ], + 'htmlErrors' => [ + 'label' => 'html_errors', + 'required' => $f3->get('REQUIREMENTS.PHP.HTML_ERRORS'), + 'version' => (int)ini_get('html_errors'), + 'check' => (bool)ini_get('html_errors') == (bool)$f3->get('REQUIREMENTS.PHP.HTML_ERRORS'), + 'tooltip' => 'Formatted HTML StackTrace on error.' + ], + [ + 'label' => 'Session' + ], + 'sessionSaveHandler' => [ + 'label' => 'save_handler', + 'version' => ini_get('session.save_handler'), + 'check' => true, + 'tooltip' => 'PHP Session save handler (Redis is preferred).' + ], + 'sessionSavePath' => [ + 'label' => 'session.save_path', + 'version' => ini_get('session.save_path'), + 'check' => true, + 'tooltip' => 'PHP Session save path (Redis is preferred).' + ], + 'sessionName' => [ + 'label' => 'session.name', + 'version' => ini_get('session.name'), + 'check' => true, + 'tooltip' => 'PHP Session name.' + ] + ]; + + return $phpConfig; + } + + /** + * get default map config + * @param \Base $f3 + * @return array + */ + protected function getMapsDefaultConfig(\Base $f3): array { + $matrix = \Matrix::instance(); + $mapsDefaultConfig = (array)Config::getMapsDefaultConfig(); + $matrix->transpose($mapsDefaultConfig); + + $mapConfig = ['mapTypes' => array_keys(reset($mapsDefaultConfig))]; + + foreach($mapsDefaultConfig as $option => $defaultConfig){ + $tooltip = ''; + switch($option){ + case 'lifetime': + $label = 'Map lifetime (days)'; + $tooltip = 'Unchanged/inactive maps get auto deleted afterwards (cronjob).'; + break; + case 'max_count': + $label = 'Max. maps count/user'; + break; + case 'max_shared': + $label = 'Map share limit/map'; + $tooltip = 'E.g. A Corp map can be shared with X other corps.'; + break; + case 'max_systems': + $label = 'Max. systems count/map'; + break; + case 'log_activity_enabled': + $label = ' Activity statistics'; + $tooltip = 'If "enabled", map admins can enable user statistics for a map.'; + break; + case 'log_history_enabled': + $label = ' History log files'; + $tooltip = 'If "enabled", map admins can pipe map logs to file. (one file per map)'; + break; + case 'send_history_slack_enabled': + $label = ' History log Slack'; + $tooltip = 'If "enabled", map admins can set a Slack channel were map logs get piped to.'; + break; + case 'send_rally_slack_enabled': + $label = ' Rally point poke Slack'; + $tooltip = 'If "enabled", map admins can set a Slack channel for rally point pokes.'; + break; + case 'send_rally_mail_enabled': + $label = ' Rally point poke Email'; + $tooltip = 'If "enabled", rally point pokes can be send by Email (SMTP config + recipient address required).'; + break; + default: + $label = 'unknown'; + } + + $mapsDefaultConfig[$option] = [ + 'label' => $label, + 'tooltip' => $tooltip, + 'data' => $defaultConfig + ]; + } + + $mapConfig['mapConfig'] = $mapsDefaultConfig; + + return $mapConfig; + } + /** * get database connection information * @param \Base $f3 @@ -611,7 +752,9 @@ class Setup extends Controller { $dbConnected = false; // DB type (e.g. MySql,..) $dbDriver = 'unknown'; - // enable database ::setup() function in UI + // enable database ::create() function on UI + $dbCreate = false; + // enable database ::setup() function on UI $dbSetupEnable = false; // check of everything is OK (connection, tables, columns, indexes,..) $dbStatusCheckCount = 0; @@ -622,7 +765,9 @@ class Setup extends Controller { // get DB config $dbConfigValues = Config::getDatabaseConfig($dbKey); // check DB for valid connection - $db = DB\Database::instance()->getDB($dbKey); + $db = $this->dbLib->getDB($dbKey); + // collection for errors + $dbErrors = []; // check config that does NOT require a valid DB connection switch($dbKey){ @@ -631,8 +776,9 @@ class Setup extends Controller { case 'CCP': $dbLabel = 'EVE-Online [SDE]'; break; } - $dbName = $dbConfigValues['NAME']; - $dbUser = $dbConfigValues['USER']; + $dbName = $dbConfigValues['NAME']; + $dbUser = $dbConfigValues['USER']; + $dbAlias = $dbConfigValues['ALIAS']; if($db){ switch($dbKey){ @@ -858,27 +1004,47 @@ class Setup extends Controller { }else{ // DB connection failed $dbStatusCheckCount++; + + foreach($this->dbLib->getErrors($dbAlias, 10) as $dbException){ + $dbErrors[] = $dbException->getMessage(); + } + + // try to connect without! DB (-> offer option to create them) + // do not log errors (silent) + $this->dbLib->setSilent(true); + $dbServer = $this->dbLib->connectToServer($dbAlias); + $this->dbLib->setSilent(false); + if(!is_null($dbServer)){ + // connection succeeded + $dbCreate = true; + $dbDriver = $dbServer->driver(); + } } if($dbStatusCheckCount !== 0){ - $this->databaseCheck = false; + $this->databaseHasError = true; } // sort tables for better readability ksort($requiredTables); $this->databases[$dbKey]['info'] = [ - 'db' => $db, - 'label' => $dbLabel, - 'driver' => $dbDriver, - 'name' => $dbName, - 'user' => $dbUser, - 'dbConfig' => $dbConfig, - 'setupEnable' => $dbSetupEnable, - 'connected' => $dbConnected, - 'statusCheckCount' => $dbStatusCheckCount, - 'columnQueries' => $dbColumnQueries, - 'tableData' => $requiredTables + // 'db' => $db, + 'label' => $dbLabel, + 'host' => Config::getDatabaseDNSValue((string)$dbConfigValues['DNS'], 'host'), + 'port' => Config::getDatabaseDNSValue((string)$dbConfigValues['DNS'], 'port'), + 'driver' => $dbDriver, + 'name' => $dbName, + 'user' => $dbUser, + 'pass' => Util::obscureString((string)$dbConfigValues['PASS'], 8), + 'dbConfig' => $dbConfig, + 'dbCreate' => $dbCreate, + 'setupEnable' => $dbSetupEnable, + 'connected' => $dbConnected, + 'statusCheckCount' => $dbStatusCheckCount, + 'columnQueries' => $dbColumnQueries, + 'tableData' => $requiredTables, + 'errors' => $dbErrors ]; } @@ -941,24 +1107,46 @@ class Setup extends Controller { return $dbConfig; } + /** + * try to create a fresh database + * @param string $dbKey + */ + protected function createDB(string $dbKey){ + // check for valid key + if(!empty($this->databases[$dbKey])){ + // disable logging (we expect the DB connect to fail -> no db created) + $this->dbLib->setSilent(true); + // try to connect + $db = $this->dbLib->getDB($dbKey); + // enable logging + $this->dbLib->setSilent(false, true); + if(is_null($db)){ + // try create new db + $db = $this->dbLib->createDB($dbKey); + if(is_null($db)){ + foreach($this->dbLib->getErrors($dbKey, 5) as $error){ + // ... no further error handling here -> check log files + //$error->getMessage() + } + } + } + } + } + /** * init the complete database * - create tables * - create indexes * - set default static values - * @param $dbKey + * @param string $dbKey * @return array */ - protected function bootstrapDB($dbKey){ - $db = DB\Database::instance()->getDB($dbKey); - + protected function bootstrapDB(string $dbKey){ + $db = $this->dbLib->getDB($dbKey); $checkTables = []; if($db){ - // set/change default "character set" and "collation" - $db->exec('ALTER DATABASE ' . $db->quotekey($db->name()) - . ' CHARACTER SET ' . self::getRequiredMySqlVariables('CHARACTER_SET_DATABASE') - . ' COLLATE ' . self::getRequiredMySqlVariables('COLLATION_DATABASE') - ); + // set some default config for this database + DB\Database::prepareDatabase($db); // setup tables foreach($this->databases[$dbKey]['models'] as $modelClass){ @@ -976,10 +1164,10 @@ class Setup extends Controller { // $ttl for health check $ttl = 600; - $heachCheckToken = microtime(true); + $healthCheckToken = microtime(true); // ping TCP Socket with checkToken - self::checkTcpSocket($ttl, $heachCheckToken); + self::checkTcpSocket($ttl, $healthCheckToken); $socketInformation = [ 'tcpSocket' => [ @@ -1004,7 +1192,7 @@ class Setup extends Controller { 'check' => !empty( $ttl ) ] ], - 'token' => $heachCheckToken + 'token' => $healthCheckToken ], 'webSocket' => [ 'label' => 'WebSocket (clients) [HTTP]', @@ -1026,78 +1214,77 @@ class Setup extends Controller { * @return array */ protected function getIndexData(){ - // active DB and tables are required for obtain index data - if( $this->databaseCheck ){ + if(!$this->databaseHasError){ $indexInfo = [ 'SystemNeighbourModel' => [ - 'action' => [ + 'task' => [ [ - 'task' => 'buildIndex', + 'action' => 'buildIndex', 'label' => 'build', 'icon' => 'fa-refresh', 'btn' => 'btn-primary' ] ], 'table' => Model\BasicModel::getNew('SystemNeighbourModel')->getTable(), - 'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() ) + 'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() ) ], 'WormholeModel' => [ - 'action' => [ + 'task' => [ [ - 'task' => 'exportTable', + 'action' => 'exportTable', 'label' => 'export', 'icon' => 'fa-download', 'btn' => 'btn-default' ],[ - 'task' => 'importTable', + 'action' => 'importTable', 'label' => 'import', 'icon' => 'fa-upload', 'btn' => 'btn-primary' ] ], 'table' => Model\BasicModel::getNew('WormholeModel')->getTable(), - 'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() ) + 'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() ) ], 'SystemWormholeModel' => [ - 'action' => [ + 'task' => [ [ - 'task' => 'exportTable', + 'action' => 'exportTable', 'label' => 'export', 'icon' => 'fa-download', 'btn' => 'btn-default' ],[ - 'task' => 'importTable', + 'action' => 'importTable', 'label' => 'import', 'icon' => 'fa-upload', 'btn' => 'btn-primary' ] ], 'table' => Model\BasicModel::getNew('SystemWormholeModel')->getTable(), - 'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() ) + 'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() ) ], 'ConstellationWormholeModel' => [ - 'action' => [ + 'task' => [ [ - 'task' => 'exportTable', + 'action' => 'exportTable', 'label' => 'export', 'icon' => 'fa-download', 'btn' => 'btn-default' ],[ - 'task' => 'importTable', + 'action' => 'importTable', 'label' => 'import', 'icon' => 'fa-upload', 'btn' => 'btn-primary' ] ], 'table' => Model\BasicModel::getNew('ConstellationWormholeModel')->getTable(), - 'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() ) + 'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() ) ] ]; }else{ $indexInfo = [ 'SystemNeighbourModel' => [ - 'action' => [], + 'task' => [], 'table' => 'Fix database errors first!' ] ]; @@ -1178,7 +1365,7 @@ class Setup extends Controller { /** * import table data from existing dump file (e.g *.csv) - * @param $modelClass + * @param string $modelClass * @return bool * @throws \Exception */ @@ -1189,7 +1376,7 @@ class Setup extends Controller { /** * export table data - * @param $modelClass + * @param string $modelClass * @throws \Exception */ protected function exportTable($modelClass){ diff --git a/app/main/cron/cache.php b/app/main/cron/cache.php index a635fb8d..63832068 100644 --- a/app/main/cron/cache.php +++ b/app/main/cron/cache.php @@ -12,11 +12,25 @@ use data\filesystem\Search; class Cache { - const LOG_TEXT = '%s [%\'_10s] files, size [%\'_10s] byte, not writable [%\'_10s] files, errors [%\'_10s], exec (%.3Fs)'; + const LOG_TEXT = '%s [%\'_10s] files, size [%\'_10s] byte, not writable [%\'_10s] files, errors [%\'_10s], exec (%.3Fs)'; + /** + * default max expire for files (seconds) + */ + const CACHE_EXPIRE_MAX = 864000; + + /** + * @param \Base $f3 + * @return int + */ + protected function getExpireMaxTime(\Base $f3): int { + $expireTime = (int)$f3->get('PATHFINDER.CACHE.EXPIRE_MAX'); + return ($expireTime >= 0) ? $expireTime : self::CACHE_EXPIRE_MAX; + } + /** * clear expired cached files - * >> >php index.php "/cron/deleteExpiredCacheData" + * >> php index.php "/cron/deleteExpiredCacheData" * @param \Base $f3 */ function deleteExpiredData(\Base $f3){ @@ -25,8 +39,8 @@ class Cache { // cache dir (dir is recursively searched...) $cacheDir = $f3->get('TEMP'); - $filterTime = (int)strtotime('-' . $f3->get('PATHFINDER.CACHE.EXPIRE_MAX') . ' seconds'); - $expiredFiles = Search::getFilesByMTime($cacheDir, $filterTime); + $filterTime = (int)strtotime('-' . $this->getExpireMaxTime($f3) . ' seconds'); + $expiredFiles = Search::getFilesByMTime($cacheDir, $filterTime, Search::DEFAULT_FILE_LIMIT); $deletedFiles = 0; $deletedSize = 0; @@ -36,16 +50,18 @@ class Cache { /** * @var $file \SplFileInfo */ - if( $file->isWritable() ){ - $tmpSize = $file->getSize(); - if( unlink($file->getRealPath()) ){ - $deletedSize += $tmpSize; - $deletedFiles++; + if($file->isFile()){ + if( $file->isWritable() ){ + $tmpSize = $file->getSize(); + if( unlink($file->getRealPath()) ){ + $deletedSize += $tmpSize; + $deletedFiles++; + }else{ + $deleteErrors++; + } }else{ - $deleteErrors++; + $notWritableFiles++; } - }else{ - $notWritableFiles++; } } diff --git a/app/main/cron/mapupdate.php b/app/main/cron/mapupdate.php index 3882ce3d..74d53f1c 100644 --- a/app/main/cron/mapupdate.php +++ b/app/main/cron/mapupdate.php @@ -8,6 +8,7 @@ namespace cron; use DB; +use lib\Config; use Model; class MapUpdate { @@ -23,19 +24,20 @@ class MapUpdate { * @param \Base $f3 */ function deactivateMapData(\Base $f3){ - $privateMapLifetime = (int)$f3->get('PATHFINDER.MAP.PRIVATE.LIFETIME'); + $privateMapLifetime = (int)Config::getMapsDefaultConfig('private.lifetime'); if($privateMapLifetime > 0){ $pfDB = DB\Database::instance()->getDB('PF'); - - $sqlDeactivateExpiredMaps = "UPDATE map SET + if($pfDB){ + $sqlDeactivateExpiredMaps = "UPDATE map SET active = 0 WHERE map.active = 1 AND map.typeId = 2 AND TIMESTAMPDIFF(DAY, map.updated, NOW() ) > :lifetime"; - $pfDB->exec($sqlDeactivateExpiredMaps, ['lifetime' => $privateMapLifetime]); + $pfDB->exec($sqlDeactivateExpiredMaps, ['lifetime' => $privateMapLifetime]); + } } } @@ -45,18 +47,31 @@ class MapUpdate { * @param \Base $f3 */ function deleteMapData(\Base $f3){ - $pfDB = DB\Database::instance()->getDB('PF'); + $deletedMapsCount = 0; - $sqlDeleteDisabledMaps = "DELETE FROM + if($pfDB){ + $sqlDeleteDisabledMaps = "SELECT + id + FROM map WHERE map.active = 0 AND TIMESTAMPDIFF(DAY, map.updated, NOW() ) > :deletion_time"; - $pfDB->exec($sqlDeleteDisabledMaps, ['deletion_time' => self::DAYS_UNTIL_MAP_DELETION]); + $disabledMaps = $pfDB->exec($sqlDeleteDisabledMaps, ['deletion_time' => self::DAYS_UNTIL_MAP_DELETION]); - $deletedMapsCount = $pfDB->count(); + if($deletedMapsCount = $pfDB->count()){ + $mapModel = Model\BasicModel::getNew('MapModel'); + foreach($disabledMaps as $data){ + $mapModel->getById( (int)$data['id'], 3, false ); + if( !$mapModel->dry() ){ + $mapModel->erase(); + } + $mapModel->reset(); + } + } + } // Log ------------------------ $log = new \Log('cron_' . __FUNCTION__ . '.log'); @@ -73,8 +88,8 @@ class MapUpdate { if($eolExpire > 0){ $pfDB = DB\Database::instance()->getDB('PF'); - - $sql = "SELECT + if($pfDB){ + $sql = "SELECT `con`.`id` FROM `connection` `con` INNER JOIN @@ -85,20 +100,21 @@ class MapUpdate { TIMESTAMPDIFF(SECOND, `con`.`eolUpdated`, NOW() ) > :expire_time "; - $connectionsData = $pfDB->exec($sql, [ - 'deleteEolConnections' => 1, - 'expire_time' => $eolExpire - ]); + $connectionsData = $pfDB->exec($sql, [ + 'deleteEolConnections' => 1, + 'expire_time' => $eolExpire + ]); - if($connectionsData){ - /** - * @var $connection Model\ConnectionModel - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($connectionsData as $data){ - $connection->getById( (int)$data['id'] ); - if( !$connection->dry() ){ - $connection->erase(); + if($connectionsData){ + /** + * @var $connection Model\ConnectionModel + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + foreach($connectionsData as $data){ + $connection->getById( (int)$data['id'] ); + if( !$connection->dry() ){ + $connection->erase(); + } } } } @@ -115,8 +131,8 @@ class MapUpdate { if($whExpire > 0){ $pfDB = DB\Database::instance()->getDB('PF'); - - $sql = "SELECT + if($pfDB){ + $sql = "SELECT `con`.`id` FROM `connection` `con` INNER JOIN @@ -128,21 +144,22 @@ class MapUpdate { TIMESTAMPDIFF(SECOND, `con`.`created`, NOW() ) > :expire_time "; - $connectionsData = $pfDB->exec($sql, [ - 'deleteExpiredConnections' => 1, - 'scope' => 'wh', - 'expire_time' => $whExpire - ]); + $connectionsData = $pfDB->exec($sql, [ + 'deleteExpiredConnections' => 1, + 'scope' => 'wh', + 'expire_time' => $whExpire + ]); - if($connectionsData){ - /** - * @var $connection Model\ConnectionModel - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($connectionsData as $data){ - $connection->getById( (int)$data['id'] ); - if( !$connection->dry() ){ - $connection->erase(); + if($connectionsData){ + /** + * @var $connection Model\ConnectionModel + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + foreach($connectionsData as $data){ + $connection->getById( (int)$data['id'] ); + if( !$connection->dry() ){ + $connection->erase(); + } } } } @@ -159,8 +176,8 @@ class MapUpdate { if($signatureExpire > 0){ $pfDB = DB\Database::instance()->getDB('PF'); - - $sqlDeleteExpiredSignatures = "DELETE `sigs` FROM + if($pfDB){ + $sqlDeleteExpiredSignatures = "DELETE `sigs` FROM `system_signature` `sigs` INNER JOIN `system` ON `system`.`id` = `sigs`.`systemId` @@ -169,7 +186,8 @@ class MapUpdate { TIMESTAMPDIFF(SECOND, `sigs`.`updated`, NOW() ) > :lifetime "; - $pfDB->exec($sqlDeleteExpiredSignatures, ['lifetime' => $signatureExpire]); + $pfDB->exec($sqlDeleteExpiredSignatures, ['lifetime' => $signatureExpire]); + } } } diff --git a/app/main/data/file/filehandler.php b/app/main/data/file/filehandler.php new file mode 100644 index 00000000..be313301 --- /dev/null +++ b/app/main/data/file/filehandler.php @@ -0,0 +1,90 @@ + Each row is a JSON object + * @param string $sourceFile + * @param int $offset + * @param int $limit + * @param null|callable $formatter + * @return array + */ + public static function readLogFile( + string $sourceFile, + int $offset = self::LOG_FILE_OFFSET, + int $limit = self::LOG_FILE_LIMIT, + $formatter = null + ): array { + $data = []; + + if(is_file($sourceFile)){ + if(is_readable($sourceFile)){ + $file = new ReverseSplFileObject($sourceFile, $offset); + $file->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY); + + foreach( new \LimitIterator($file, 0, $limit) as $i => $rowData){ + if( !empty($rowDataObj = (array)json_decode($rowData, true)) ){ + if(is_callable($formatter)){ + $formatter($rowDataObj); + } + $data[] = $rowDataObj; + } + } + }else{ + \Base::instance()->error(500, sprintf(self::ERROR_STREAM_READABLE, $sourceFile)); + } + } + + return $data; + } + + /** + * validate offset + * @param int $offset + * @return int + */ + public static function validateOffset(int $offset): int{ + if( + $offset < self::LOG_FILE_OFFSET_MIN || + $offset > self::LOG_FILE_OFFSET_MAX + ){ + $offset = self::LOG_FILE_OFFSET; + } + return $offset; + } + + /** + * validate limit + * @param int $limit + * @return int + */ + public static function validateLimit(int $limit): int{ + if( + $limit < self::LOG_FILE_LIMIT_MIN || + $limit > self::Log_File_LIMIT_MAX + ){ + $limit = self::LOG_FILE_LIMIT; + } + return $limit; + } +} \ No newline at end of file diff --git a/app/main/data/file/reversesplfileobject.php b/app/main/data/file/reversesplfileobject.php new file mode 100644 index 00000000..29cc4ea6 --- /dev/null +++ b/app/main/data/file/reversesplfileobject.php @@ -0,0 +1,219 @@ + 'start with 2nd last line') + * @var int + */ + protected $offset = 0; + + /** + * total lines found in file + * @var int + */ + protected $lineCount = 0; + + /** + * empty lines found in file + * @var int + */ + protected $lineCountEmpty = 0; + + /** + * current pointer position + * @var int + */ + protected $pointer = 0; + + /** + * position increments when valid row data found + * @var + */ + protected $position; + + /** + * control characters + * @var array + */ + protected $eol = ["\r", "\n"]; + + public function __construct($sourceFile, $offset = 0){ + parent::__construct($sourceFile); + + // set total line count of the file + $this->setLineCount(); + + //Seek to the first position of the file and record its position + //Should be 0 + $this->fseek(0); + $this->begin = $this->ftell(); + $this->offset = $offset; + + //Seek to the last position from the end of the file + //This varies depending on the file + $this->fseek($this->pointer, SEEK_END); + } + + /** + * reverse rewind file. + */ + public function rewind(){ + //Set the line position to 0 - First Line + $this->position = 0; + + //Reset the file pointer to the end of the file minus 1 character. "0" == false + $this->fseek(-1, SEEK_END); + + $this->findLineBegin(); + //... File pointer is now at the beginning of the last line that contains data + + // add custom line offset + if($this->offset){ + // calculate offset start line + $offsetLine = $this->lineCount - $this->lineCountEmpty - $this->offset; + + if($offsetLine > 0){ + // row is zero based + $offsetIndex = $offsetLine - 1; + + parent::seek($offsetIndex); + // seek() sets pointer to next line... set it back to previous + $this->fseek(-2, SEEK_CUR); + + $this->findLineBegin(); + //... File pointer is now at the beginning of the last line that contains data from $offset + }else{ + // negative offsetLine -> invalid! + $this->pointer = $this->begin -1; + } + + } + } + + /** + * Return the current line after the file pointer + * @return string + */ + public function current(){ + return trim($this->fgets()); + } + + /** + * Return the current key of the line we're on + * These go in reverse order + * @return mixed + */ + public function key(){ + return $this->position; + } + + /** + * move one line up + */ + public function next(){ + //Step the file pointer back one step to the last letter of the previous line + --$this->pointer; + if($this->pointer < $this->begin){ + return; + } + + $this->fseek($this->pointer); + + $this->findLineBegin(); + + //File pointer is now on the next previous line + //Increment the line position + ++$this->position; + } + + /** + * Check the current file pointer to make sure we are not at the beginning of the file + * @return bool + */ + public function valid(){ + return ($this->pointer >= $this->begin); + } + + /** + * seek to previous lines + * @param int $lineCount + */ + public function seek($lineCount){ + for($i = 0; $i < $lineCount; $i++){ + $this->next(); + } + } + + /** + * move pointer to line begin + * -> skip line breaks + */ + private function findLineBegin(){ + //Check the character over and over till we hit another new line + $c = $this->fgetc(); + + // skip empty lines + while(in_array($c, $this->eol)){ + $this->fseek(-2, SEEK_CUR); + if(!$this->pointer = $this->ftell()){ + break; + } + $c = $this->fgetc(); + + $this->lineCountEmpty++; + } + + //Check the last character to make sure it is not a new line + while(!in_array($c, $this->eol)){ + $this->fseek(-2, SEEK_CUR); + if(!$this->pointer = $this->ftell()){ + break; + } + $c = $this->fgetc(); + } + } + + /** + * set total line count. No matter if there are empty lines in between + */ + private function setLineCount(){ + // Store flags and position + $flags = $this->getFlags(); + $currentPointer = $this->ftell(); + + // Prepare count by resetting flags as READ_CSV for example make the tricks very slow + $this->setFlags(null); + + // Go to the larger INT we can as seek will not throw exception, errors, notice if we go beyond the bottom line + //$this->seek(PHP_INT_MAX); + parent::seek(PHP_INT_MAX); + + // We store the key position + // As key starts at 0, we add 1 + $this->lineCount = parent::key() + 1; + + // We move to old position + // As seek method is longer with line number < to the max line number, it is better to count at the beginning of iteration + //parent::seek($currentPointer); + $this->fseek($currentPointer); + + // Re set flags + $this->setFlags($flags); + } +} \ No newline at end of file diff --git a/app/main/data/filesystem/search.php b/app/main/data/filesystem/search.php index 36c1d8b0..d94958fe 100644 --- a/app/main/data/filesystem/search.php +++ b/app/main/data/filesystem/search.php @@ -11,20 +11,26 @@ namespace data\filesystem; class Search { + /** + * max file count that should be deleted in this session + */ + const DEFAULT_FILE_LIMIT = 1000; + /** * timestamp (seconds) filter files by mTime() * -> default = "no filter" * @var int */ - static $filterTime = 0; + static $filterTime = 0; /** * recursive file filter by mTime * @param string $dir * @param int $mTime - * @return array|\RecursiveCallbackFilterIterator + * @param int $limit + * @return array|\LimitIterator */ - static function getFilesByMTime($dir, $mTime = null){ + static function getFilesByMTime(string $dir, $mTime = null, $limit = self::DEFAULT_FILE_LIMIT){ $files = []; if(is_dir($dir)){ @@ -53,7 +59,8 @@ class Search { return false; }); - $files = new \RecursiveIteratorIterator($files); + // limit max files + $files = new \LimitIterator($files, 0, $limit); } return $files; diff --git a/app/main/db/database.php b/app/main/db/database.php index f6049ff9..f5f09bcb 100644 --- a/app/main/db/database.php +++ b/app/main/db/database.php @@ -7,45 +7,71 @@ */ namespace DB; -use Controller; use controller\LogController; use lib\Config; class Database extends \Prefab { + /** + * if true, errors will not get logged + * @var bool + */ + private $silent = false; - function __construct($database = 'PF'){ - // set database - $this->setDB($database); + /** + * @var array + */ + private $errors = []; + + /** + * connect to the DB server itself -> NO database is used + * -> can be used to check if a certain DB exists without connecting to it directly + * @param string $dbKey + * @return SQL|null + */ + public function connectToServer(string $dbKey = 'PF'){ + $dbConfig = Config::getDatabaseConfig($dbKey); + $dbConfig['DNS'] = str_replace(';dbname=', '', $dbConfig['DNS'] ); + $dbConfig['NAME'] = ''; + return call_user_func_array([$this, 'connect'], $dbConfig); } /** - * set database - * @param string $database - * @return SQL + * tries to create a database if not exists + * -> DB user needs rights to create a DB + * @param string $dbKey + * @return SQL|null */ - public function setDB($database = 'PF'){ - $f3 = \Base::instance(); + public function createDB(string $dbKey = 'PF'){ + $db = null; + $dbConfig = Config::getDatabaseConfig($dbKey); + // remove database from $dsn (we want to crate it) + $newDbName = $dbConfig['NAME']; + if(!empty($newDbName)){ + $dbConfig['NAME'] = ''; + $dbConfig['DNS'] = str_replace(';dbname=', '', $dbConfig['DNS'] ); - // "Hive" Key for DB storage - $dbHiveKey = $this->getDbHiveKey($database); + $db = call_user_func_array([$this, 'connect'], $dbConfig); - // check if DB connection already exists - if( !$f3->exists($dbHiveKey, $db) ){ - $dbConfig = Config::getDatabaseConfig($database); + if(!is_null($db)){ + $schema = new SQL\Schema($db); + if(!in_array($newDbName, $schema->getDatabases())){ + $db->exec("CREATE DATABASE IF NOT EXISTS + `" . $newDbName . "` DEFAULT CHARACTER SET utf8 + COLLATE utf8_general_ci;"); + $db->exec("USE `" . $newDbName . "`"); - $db = call_user_func_array([$this, 'connect'], $dbConfig); - - if( !is_null($db) ){ - // set DB timezone to UTC +00:00 (eve server time) - $db->exec('SET @@session.time_zone = "+00:00";'); - - // set default storage engine - $db->exec('SET @@session.default_storage_engine = "' . - Controller\Controller::getRequiredMySqlVariables('DEFAULT_STORAGE_ENGINE') . '"'); - - // store DB object - $f3->set($dbHiveKey, $db); + // check if DB create was successful + $dbCheck = $db->exec("SELECT DATABASE()"); + if( + !empty($dbCheck[0]) && + !empty($checkDbName = reset($dbCheck[0])) && + $checkDbName == $newDbName + ){ + self::prepareDBConnection($db); + self::prepareDatabase($db); + } + } } } @@ -54,14 +80,20 @@ class Database extends \Prefab { /** * get database - * @param string $database - * @return SQL + * @param string $dbKey + * @return SQL|null */ - public function getDB($database = 'PF'){ + public function getDB(string $dbKey = 'PF'){ $f3 = \Base::instance(); - $dbHiveKey = $this->getDbHiveKey($database); + // "Hive" Key for DB object cache + $dbHiveKey = $this->getDbHiveKey($dbKey); if( !$f3->exists($dbHiveKey, $db) ){ - $db = $this->setDB($database); + $dbConfig = Config::getDatabaseConfig($dbKey); + $db = call_user_func_array([$this, 'connect'], $dbConfig); + if(!is_null($db)){ + self::prepareDBConnection($db); + $f3->set($dbHiveKey, $db); + } } return $db; @@ -69,23 +101,23 @@ class Database extends \Prefab { /** * get a unique hive key for each DB connection - * @param $database + * @param $dbKey * @return string */ - protected function getDbHiveKey($database){ - return 'DB_' . $database; + protected function getDbHiveKey($dbKey){ + return 'DB_' . $dbKey; } - /** * connect to a database - * @param $dns - * @param $name - * @param $user - * @param $password - * @return SQL + * @param string $dns + * @param string $name + * @param string $user + * @param string $password + * @param string $alias + * @return SQL|null */ - protected function connect($dns, $name, $user, $password){ + protected function connect($dns, $name, $user, $password, $alias){ $db = null; $f3 = \Base::instance(); @@ -109,9 +141,10 @@ class Database extends \Prefab { $options ); }catch(\PDOException $e){ - // DB connection error - // -> log it - self::getLogger()->write($e->getMessage()); + $this->pushError($alias, $e); + if(!$this->isSilent()){ + self::getLogger()->write($e); + } } return $db; @@ -119,22 +152,22 @@ class Database extends \Prefab { /** * get all table names from a DB - * @param string $database + * @param string $dbKey * @return array|bool */ - public function getTables($database = 'PF'){ - $schema = new SQL\Schema( $this->getDB($database) ); + public function getTables($dbKey = 'PF'){ + $schema = new SQL\Schema( $this->getDB($dbKey) ); return $schema->getTables(); } /** * checks whether a table exists on a DB or not * @param $table - * @param string $database + * @param string $dbKey * @return bool */ - public function tableExists($table, $database = 'PF'){ - $tableNames = $this->getTables($database); + public function tableExists($table, $dbKey = 'PF'){ + $tableNames = $this->getTables($dbKey); return in_array($table, $tableNames); } @@ -142,13 +175,13 @@ class Database extends \Prefab { * get current row (data) count for an existing table * -> returns 0 if table not exists or empty * @param $table - * @param string $database + * @param string $dbKey * @return int */ - public function getRowCount($table, $database = 'PF') { + public function getRowCount($table, $dbKey = 'PF') { $count = 0; - if( $this->tableExists($table, $database) ){ - $db = $this->getDB($database); + if( $this->tableExists($table, $dbKey) ){ + $db = $this->getDB($dbKey); $countRes = $db->exec("SELECT COUNT(*) `num` FROM " . $db->quotekey($table)); if(isset($countRes[0]['num'])){ $count = (int)$countRes[0]['num']; @@ -157,6 +190,98 @@ class Database extends \Prefab { return $count; } + /** + * @return bool + */ + public function isSilent() : bool{ + return $this->silent; + } + + /** + * set "silent" mode (no error logging) + * -> optional clear $this->errors + * @param bool $silent + * @param bool $clearErrors + */ + public function setSilent(bool $silent, bool $clearErrors = false){ + $this->silent = $silent; + if($clearErrors){ + $this->errors = []; + } + } + + /** + * push new Exception into static error history + * @param string $alias + * @param \PDOException $e + */ + protected function pushError(string $alias, \PDOException $e){ + if(!is_array($this->errors[$alias])){ + $this->errors[$alias] = []; + } + + // prevent adding same errors twice + if(!empty($this->errors[$alias])){ + $lastError = array_values($this->errors[$alias])[0]; + if($lastError->getMessage() === $e->getMessage()){ + return; + } + } + + array_unshift($this->errors[$alias], $e); + if(count($this->errors[$alias]) > 5){ + $this->errors[$alias] = array_pop($this->errors[$alias]); + } + } + + /** + * get last recent Exceptions from error history + * @param string $alias + * @param int $limit + * @return \PDOException[] + */ + public function getErrors(string $alias, int $limit = 1){ + return array_slice((array)$this->errors[$alias] , 0, $limit); + } + + /** + * prepare current DB + * -> set session connection variables + * @param SQL $db + */ + public static function prepareDBConnection(SQL &$db){ + // set DB timezone to UTC +00:00 (eve server time) + $db->exec('SET @@session.time_zone = "+00:00";'); + + // set default storage engine + $db->exec('SET @@session.default_storage_engine = "' . + self::getRequiredMySqlVariables('DEFAULT_STORAGE_ENGINE') . '"'); + } + + /** + * set some default config for current DB + * @param SQL $db + */ + public static function prepareDatabase(SQL &$db){ + if($db->name()){ + // set/change default "character set" and "collation" + $db->exec('ALTER DATABASE ' . $db->quotekey($db->name()) + . ' CHARACTER SET ' . self::getRequiredMySqlVariables('CHARACTER_SET_DATABASE') + . ' COLLATE ' . self::getRequiredMySqlVariables('COLLATION_DATABASE') + ); + } + } + + /** + * get required MySQL variable value + * @param string $key + * @return string|null + */ + public static function getRequiredMySqlVariables(string $key){ + \Base::instance()->exists('REQUIREMENTS[MYSQL][VARS][' . $key . ']', $data); + return $data; + } + /** * get logger for DB logging * @return \Log diff --git a/app/main/exception/baseexception.php b/app/main/exception/baseexception.php index 9262e4c4..af4b580c 100644 --- a/app/main/exception/baseexception.php +++ b/app/main/exception/baseexception.php @@ -11,11 +11,12 @@ namespace Exception; class BaseException extends \Exception { - const VALIDATION_FAILED = 403; - const REGISTRATION_FAILED = 403; - const CONFIGURATION_FAILED = 500; + const VALIDATION_EXCEPTION = 403; + const REGISTRATION_EXCEPTION = 403; + const CONFIG_VALUE_EXCEPTION = 500; + const DB_EXCEPTION = 500; - public function __construct($message, $code = 0){ + public function __construct(string $message, int $code = 0){ parent::__construct($message, $code); } diff --git a/app/main/exception/databaseexception.php b/app/main/exception/databaseexception.php new file mode 100644 index 00000000..a6a72bec --- /dev/null +++ b/app/main/exception/databaseexception.php @@ -0,0 +1,16 @@ +setField($field); } } \ No newline at end of file diff --git a/app/main/exception/validationexception.php b/app/main/exception/validationexception.php index 74a3db58..200f4d0c 100644 --- a/app/main/exception/validationexception.php +++ b/app/main/exception/validationexception.php @@ -11,27 +11,41 @@ namespace Exception; class ValidationException extends BaseException { + /** + * table column that triggers the exception + * @var string + */ private $field; /** - * @return mixed + * @return string */ - public function getField(){ + public function getField(): string { return $this->field; } /** - * @param mixed $field + * @param string $field */ - public function setField($field){ + public function setField(string $field){ $this->field = $field; } - public function __construct($message, $field = 0){ - - parent::__construct($message, self::VALIDATION_FAILED); - + public function __construct(string $message, string $field = ''){ + parent::__construct($message, self::VALIDATION_EXCEPTION); $this->setField($field); } + + /** + * get error object + * @return \stdClass + */ + public function getError(){ + $error = (object) []; + $error->type = 'error'; + $error->field = $this->getField(); + $error->message = $this->getMessage(); + return $error; + } } \ No newline at end of file diff --git a/app/main/lib/Monolog.php b/app/main/lib/Monolog.php new file mode 100644 index 00000000..b28c1e48 --- /dev/null +++ b/app/main/lib/Monolog.php @@ -0,0 +1,216 @@ + 'Monolog\Formatter\LineFormatter', + 'json' => 'Monolog\Formatter\JsonFormatter', + 'html' => 'Monolog\Formatter\HtmlFormatter', + 'mail' => 'Lib\Logging\Formatter\MailFormatter' + ]; + + const HANDLER = [ + 'stream' => 'Monolog\Handler\StreamHandler', + 'mail' => 'Monolog\Handler\SwiftMailerHandler', + 'slackMap' => 'Lib\Logging\Handler\SlackMapWebhookHandler', + 'slackRally' => 'Lib\Logging\Handler\SlackRallyWebhookHandler', + 'zmq' => 'Lib\Logging\Handler\ZMQHandler' + ]; + + const PROCESSOR = [ + 'psr' => 'Monolog\Processor\PsrLogMessageProcessor' + ]; + + /** + * @var Logging\LogCollection[][]|Logging\MapLog[][] + */ + private $logs = [ + 'solo' => [], + 'groups' => [] + ]; + + public function __construct(){ + // set timezone for all Logger instances + if(class_exists(Logger::class)){ + if( is_callable($getTimezone = \Base::instance()->get('getTimeZone')) ){ + Logger::setTimezone($getTimezone()); + }; + }else{ + LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, Logger::class)); + } + } + + /** + * buffer log object, add to objectStorage collection + * -> this buffered data can be stored/logged somewhere (e.g. DB/file) at any time + * -> should be cleared afterwards! + * @param Logging\AbstractLog $log + */ + public function push(Logging\AbstractLog $log){ + // check whether $log should be "grouped" by common handlers + if($log->isGrouped()){ + $groupHash = $log->getGroupHash(); + + if(!isset($this->logs['groups'][$groupHash])){ + // create new log collection + // $this->logs['groups'][$groupHash] = new Logging\LogCollection($log->getChannelName()); + $this->logs['groups'][$groupHash] = new Logging\LogCollection('mapDelete'); + } + $this->logs['groups'][$groupHash]->addLog($log); + + // remove "group" handler from $log + // each log should only be logged once per handler! + $log->removeHandlerGroups(); + } + + $this->logs['solo'][] = $log; + } + + /** + * bulk process all stored logs -> send to Monolog lib + */ + public function log(){ + + foreach($this->logs as $logType => $logs){ + foreach($logs as $logKey => $log){ + $groupHash = $log->getGroupHash(); + $level = Logger::toMonologLevel($log->getLevel()); + + // add new logger to Registry if not already exists + if(Registry::hasLogger($groupHash)){ + $logger = Registry::getInstance($groupHash); + }else{ + $logger = new Logger($log->getChannelName()); + + // disable microsecond timestamps (seconds should be fine) + $logger->useMicrosecondTimestamps(true); + + // configure new $logger -------------------------------------------------------------------------- + // get Monolog Handler with Formatter config + // -> $log could have multiple handler with different Formatters + $handlerConf = $log->getHandlerConfig(); + foreach($handlerConf as $handlerKey => $formatterKey){ + // get Monolog Handler class + $handlerParams = $log->getHandlerParams($handlerKey); + $handler = $this->getHandler($handlerKey, $handlerParams); + + // get Monolog Formatter + $formatter = $this->getFormatter((string)$formatterKey); + if( $formatter instanceof FormatterInterface){ + $handler->setFormatter($formatter); + } + + if($log->hasBuffer()){ + // wrap Handler into bufferHandler + // -> bulk save all logs for this $logger + $bufferHandler = new BufferHandler($handler); + $logger->pushHandler($bufferHandler); + }else{ + $logger->pushHandler($handler); + } + } + + // get Monolog Processor config + $processorConf = $log->getProcessorConfig(); + foreach($processorConf as $processorKey => $processorCallback){ + if(is_callable($processorCallback)){ + // custom Processor callback function + $logger->pushProcessor($processorCallback); + }else{ + // get Monolog Processor class + $processor = $this->getProcessor($processorKey); + $logger->pushProcessor($processor); + } + } + + Registry::addLogger($logger, $groupHash); + } + + $logger->addRecord($level, $log->getMessage(), $log->getContext()); + } + } + + // clear log object storage + $this->logs['groups'] = []; + $this->logs['solo'] = []; + } + + /** + * get Monolog Formatter instance by key + * @param string $formatKey + * @return FormatterInterface|null + * @throws \Exception + */ + private function getFormatter(string $formatKey){ + $formatter = null; + if(!empty($formatKey)){ + if(array_key_exists($formatKey, self::FORMATTER)){ + $formatClass = self::FORMATTER[$formatKey]; + $formatter = new $formatClass(); + }else{ + throw new \Exception(sprintf(self::ERROR_FORMATTER, $formatKey)); + } + } + + return $formatter; + } + + /** + * get Monolog Handler instance by key + * @param string $handlerKey + * @param array $handlerParams + * @return HandlerInterface + * @throws \Exception + */ + private function getHandler(string $handlerKey, array $handlerParams = []): HandlerInterface{ + if(array_key_exists($handlerKey, self::HANDLER)){ + $handlerClass = self::HANDLER[$handlerKey]; + $handler = new $handlerClass(...$handlerParams); + }else{ + throw new \Exception(sprintf(self::ERROR_HANDLER, $handlerKey)); + } + + return $handler; + } + + /** + * get Monolog Processor instance by key + * @param string $processorKey + * @return callable + * @throws \Exception + */ + private function getProcessor(string $processorKey): callable { + if(array_key_exists($processorKey, self::PROCESSOR)){ + $ProcessorClass = self::PROCESSOR[$processorKey]; + $processor = new $ProcessorClass(); + }else{ + throw new \Exception(sprintf(self::ERROR_PROCESSOR, $processorKey)); + } + + return $processor; + } + + +} \ No newline at end of file diff --git a/app/main/lib/ccpclient.php b/app/main/lib/ccpclient.php index aef5a774..fa018950 100644 --- a/app/main/lib/ccpclient.php +++ b/app/main/lib/ccpclient.php @@ -16,24 +16,26 @@ class CcpClient extends \Prefab { private $apiClient; public function __construct(\Base $f3){ - $this->apiClient = $this->getClient(); + $this->apiClient = $this->getClient($f3); $f3->set('ccpClient', $this); } /** * get ApiClient instance + * @param \Base $f3 * @return ApiClient|null */ - protected function getClient(){ + protected function getClient(\Base $f3){ $client = null; - if( !class_exists(ApiClient::class) ){ - LogController::getLogger('ERROR')->write($this->getMissingClientError()); - }else{ + if(class_exists(ApiClient::class)){ $client = new ApiClient(); $client->setUrl( Config::getEnvironmentData('CCP_ESI_URL') ); $client->setDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') ); $client->setUserAgent($this->getUserAgent()); + $client->setDebugLevel($f3->get('DEBUG')); + }else{ + LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class)); } return $client; @@ -52,14 +54,6 @@ class CcpClient extends \Prefab { return $userAgent; } - /** - * get error msg for failed ApiClient() class -> Composer package not found - * @return string - */ - protected function getMissingClientError(){ - return "Class '" . ApiClient::class . "' not found. -> Check installed Composer packages.'"; - } - /** * get error msg for undefined method in ApiClient() class * @param $method @@ -86,8 +80,8 @@ class CcpClient extends \Prefab { \Base::instance()->error(501, $this->getMissingMethodError($name)); } }else{ - LogController::getLogger('ERROR')->write($this->getMissingClientError()); - \Base::instance()->error(501, $this->getMissingClientError()); + LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class)); + \Base::instance()->error(501, sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class)); } return $return; diff --git a/app/main/lib/config.php b/app/main/lib/config.php index 9aa28bb9..a663fd87 100644 --- a/app/main/lib/config.php +++ b/app/main/lib/config.php @@ -17,8 +17,11 @@ class Config extends \Prefab { const ARRAY_DELIMITER = '-'; const HIVE_KEY_PATHFINDER = 'PATHFINDER'; const HIVE_KEY_ENVIRONMENT = 'ENVIRONMENT'; + const CACHE_KEY_SOCKET_VALID = 'CACHED_SOCKET_VALID'; + const CACHE_TTL_SOCKET_VALID = 60; const ERROR_CONF_PATHFINDER = 'Config value missing in pathfinder.ini file [%s]'; + const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. -> Check installed Composer packages'; /** @@ -43,6 +46,11 @@ class Config extends \Prefab { // set hive configuration variables // -> overwrites default configuration $this->setHiveVariables($f3); + + // set global function for current DateTimeZone() + $f3->set('getTimeZone', function() use ($f3){ + return new \DateTimeZone( $f3->get('TZ') ); + }); } /** @@ -164,7 +172,6 @@ class Config extends \Prefab { static function getEnvironmentData($key){ $hiveKey = self::HIVE_KEY_ENVIRONMENT . '.' . $key; \Base::instance()->exists($hiveKey, $data); - return $data; } @@ -173,16 +180,74 @@ class Config extends \Prefab { * @param string $dbKey * @return array */ - static function getDatabaseConfig($dbKey = 'PF'){ - $dbConfKey = ($dbKey === 'PF') ? '' : $dbKey . '_'; + static function getDatabaseConfig(string $dbKey = 'PF'){ + $dbKey = strtoupper($dbKey); return [ - 'DNS' => self::getEnvironmentData('DB_' . $dbConfKey . 'DNS'), - 'NAME' => self::getEnvironmentData('DB_' . $dbConfKey . 'NAME'), - 'USER' => self::getEnvironmentData('DB_' . $dbConfKey . 'USER'), - 'PASS' => self::getEnvironmentData('DB_' . $dbConfKey . 'PASS') + 'DNS' => self::getEnvironmentData('DB_' . $dbKey . '_DNS'), + 'NAME' => self::getEnvironmentData('DB_' . $dbKey . '_NAME'), + 'USER' => self::getEnvironmentData('DB_' . $dbKey . '_USER'), + 'PASS' => self::getEnvironmentData('DB_' . $dbKey . '_PASS'), + 'ALIAS' => $dbKey ]; } + /** + * get DB config value from PDO connect $dns string + * @param string $dns + * @param string $key + * @return bool + */ + static function getDatabaseDNSValue(string $dns, string $key = 'dbname'){ + $value = false; + if(preg_match('/' . preg_quote($key, '/') . '=([[:alnum:]]+)/is', $dns, $parts)){ + $value = $parts[1]; + } + return $value; + } + + /** + * get SMTP config values + * @return \stdClass + */ + static function getSMTPConfig(): \stdClass{ + $config = new \stdClass(); + $config->host = self::getEnvironmentData('SMTP_HOST'); + $config->port = self::getEnvironmentData('SMTP_PORT'); + $config->scheme = self::getEnvironmentData('SMTP_SCHEME'); + $config->username = self::getEnvironmentData('SMTP_USER'); + $config->password = self::getEnvironmentData('SMTP_PASS'); + $config->from = [ + self::getEnvironmentData('SMTP_FROM') => self::getPathfinderData('name') + ]; + return $config; + } + + /** + * validates an SMTP config + * @param \stdClass $config + * @return bool + */ + static function isValidSMTPConfig(\stdClass $config): bool { + // validate email from either an configured array or plain string + $validateMailConfig = function($mailConf = null): bool { + $email = null; + if(is_array($mailConf)){ + reset($mailConf); + $email = key($mailConf); + }elseif(is_string($mailConf)){ + $email = $mailConf; + } + return \Audit::instance()->email($email); + }; + + return ( + !empty($config->host) && + !empty($config->username) && + $validateMailConfig($config->from) && + $validateMailConfig($config->to) + ); + } + /** * get email for notifications by hive key * @param $key @@ -196,7 +261,7 @@ class Config extends \Prefab { * get map default config values for map types (private/corp/ally) * -> read from pathfinder.ini * @param string $mapType - * @return array + * @return mixed */ static function getMapsDefaultConfig($mapType = ''){ if( $mapConfig = self::getPathfinderData('map' . ($mapType ? '.' . $mapType : '')) ){ @@ -206,6 +271,92 @@ class Config extends \Prefab { return $mapConfig; } + /** + * get custom $message for a a HTTP $status + * -> use this in addition to the very general Base::HTTP_XXX labels + * @param int $status + * @return string + */ + static function getMessageFromHTTPStatus(int $status): string { + switch($status){ + case 403: + $message = 'Access denied: User not found'; break; + default: + $message = ''; + } + return $message; + } + + /** + * check whether this installation fulfills all requirements + * -> check for ZMQ PHP extension and installed ZQM version + * -> this does NOT check versions! -> those can be verified on /setup page + * @return bool + */ + static function checkSocketRequirements(): bool { + return extension_loaded('zmq') && class_exists('ZMQ'); + } + + /** + * use this function to "validate" the socket connection. + * The result will be CACHED for a few seconds! + * This function is intended to pre-check a Socket connection if it MIGHT exists. + * No data will be send to the Socket, this function just validates if a socket is available + * -> see pingDomain() + * @return bool + */ + static function validSocketConnect(): bool{ + $valid = false; + $f3 = \Base::instance(); + + if( !$f3->exists(self::CACHE_KEY_SOCKET_VALID, $valid) ){ + if(self::checkSocketRequirements() && ($socketUrl = self::getSocketUri()) ){ + // get socket URI parts -> not elegant... + $domain = parse_url( $socketUrl, PHP_URL_SCHEME) . '://' . parse_url( $socketUrl, PHP_URL_HOST); + $port = parse_url( $socketUrl, PHP_URL_PORT); + // check connection -> get ms + $status = self::pingDomain($domain, $port); + if($status >= 0){ + // connection OK + $valid = true; + }else{ + // connection error/timeout + $valid = false; + } + }else{ + // requirements check failed or URL not valid + $valid = false; + } + + $f3->set(self::CACHE_KEY_SOCKET_VALID, $valid, self::CACHE_TTL_SOCKET_VALID); + } + + return $valid; + } + + /** + * get response time for a host in ms or -1 on error/timeout + * @param string $domain + * @param int $port + * @param int $timeout + * @return int + */ + static function pingDomain(string $domain, int $port, $timeout = 1): int { + $starttime = microtime(true); + $file = @fsockopen ($domain, $port, $errno, $errstr, $timeout); + $stoptime = microtime(true); + + if (!$file){ + // Site is down + $status = -1; + }else { + fclose($file); + $status = ($stoptime - $starttime) * 1000; + $status = floor($status); + } + return $status; + } + /** * get URI for TCP socket * @return bool|string diff --git a/app/main/lib/logging/AbstractChannelLog.php b/app/main/lib/logging/AbstractChannelLog.php new file mode 100644 index 00000000..9f865da1 --- /dev/null +++ b/app/main/lib/logging/AbstractChannelLog.php @@ -0,0 +1,87 @@ +setChannelData($channelData); + + // add log processor -> remove $channelData from log + $processorClearChannelData = function($record){ + $record['context'] = array_diff_key($record['context'], $this->getChannelData()); + return $record; + }; + + // init processorConfig. IMPORTANT: first processor gets executed at the end! + $this->processorConfig = ['clearChannelData' => $processorClearChannelData] + $this->processorConfig; + } + + /** + * @param array $channelData + */ + protected function setChannelData(array $channelData){ + $this->channelData = $channelData; + } + + /** + * @return array + */ + public function getChannelData() : array{ + return $this->channelData; + } + + /** + * @return int + */ + public function getChannelId() : int{ + return (int)$this->getChannelData()['channelId']; + } + + /** + * @return string + */ + public function getChannelName() : string{ + return (string)$this->getChannelData()['channelName']; + } + + /** + * @return array + */ + public function getData() : array{ + $data['main'] = parent::getData(); + + if(!empty($channelLogData = $this->getChannelData())){ + $channelData['channel'] = $channelLogData; + $data = $channelData + $data; + } + + return $data; + } + + /** + * @return array + */ + public function getContext(): array{ + $context = parent::getContext(); + + // add temp data (e.g. used for $message placeholder replacement + $context += $this->getChannelData(); + + return $context; + } +} \ No newline at end of file diff --git a/app/main/lib/logging/AbstractCharacterLog.php b/app/main/lib/logging/AbstractCharacterLog.php new file mode 100644 index 00000000..52a66339 --- /dev/null +++ b/app/main/lib/logging/AbstractCharacterLog.php @@ -0,0 +1,81 @@ + remove $channelData from log + $processorAddThumbData = function($record){ + $record['extra']['thumb']['url'] = $this->getThumbUrl(); + return $record; + }; + + // init processorConfig. IMPORTANT: first processor gets executed at the end! + $this->processorConfig = ['addThumbData' => $processorAddThumbData] + $this->processorConfig; + } + + /** + * CharacterModel $character + * @param CharacterModel $character + * @return LogInterface + */ + public function setCharacter(CharacterModel $character): LogInterface{ + $this->character = $character; + return $this; + } + + /** + * @return CharacterModel + */ + public function getCharacter(): CharacterModel{ + return $this->character; + } + + /** + * @return array + */ + public function getData() : array{ + $data = parent::getData(); + + if(is_object($character = $this->getCharacter())){ + $characterData['character'] = [ + 'id' => $character->_id, + 'name' => $character->name + ]; + $data = $characterData + $data; + } + + return $data; + } + + /** + * get character thumbnailUrl + * @return string + */ + protected function getThumbUrl(): string { + $url = ''; + if(is_object($character = $this->getCharacter())){ + $url = Config::getPathfinderData('api.ccp_image_server') . '/Character/' . $character->_id . '_128.jpg'; + } + + return $url; + } + +} \ No newline at end of file diff --git a/app/main/lib/logging/AbstractLog.php b/app/main/lib/logging/AbstractLog.php new file mode 100644 index 00000000..a772dd10 --- /dev/null +++ b/app/main/lib/logging/AbstractLog.php @@ -0,0 +1,549 @@ + check Monolog::HANDLER and Monolog::FORMATTER + * @var array + */ + protected $handlerConfig = ['stream' => 'line']; + + /** + * log Processors, array with either callable functions or Processor class with __invoce() method + * -> functions used to add "extra" data to a log + * @var array + */ + protected $processorConfig = ['psr' => null]; + + /** + * some handler need individual configuration parameters + * -> see $handlerConfig end getHandlerParams() + * @var array + */ + protected $handlerParamsConfig = []; + + /** + * multiple Log() objects can be marked as "grouped" + * -> Logs with Slack Handler should be grouped by map (send multiple log data in once + * @var array + */ + protected $handlerGroups = []; + + /** + * @var string + */ + protected $message = ''; + + /** + * @var string + */ + protected $action = ''; + + /** + * @var string + */ + protected $channelType = ''; + + /** + * log level from self::LEVEL + * -> private - use setLevel() to set + * @var string + */ + private $level = 'debug'; + + /** + * log tag from self::TAG + * -> private - use setTag() to set + * @var string + */ + private $tag = 'default'; + + /** + * log data (main log data) + * @var array + */ + private $data = []; + + /** + * (optional) temp data for logger (will not be stored with the log entry) + * @var array + */ + private $tmpData = []; + + /** + * buffer multiple logs with the same chanelType and store all at once + * @var bool + */ + private $buffer = true; + + + public function __construct(string $action){ + $this->setF3(); + $this->action = $action; + + // add custom log processor callback -> add "extra" (meta) data + $f3 = $this->f3; + $processorExtraData = function($record) use(&$f3){ + $record['extra'] = [ + 'path' => $f3->get('PATH'), + 'ip' => $f3->get('IP') + ]; + return $record; + }; + + // add log processor -> remove §tempData from log + $processorClearTempData = function($record){ + $record['context'] = array_diff_key($record['context'], $this->getTempData()); + return $record; + }; + + // init processorConfig. IMPORTANT: first processor gets executed at the end! + $this->processorConfig = ['cleaTempData' => $processorClearTempData] + [ 'addExtra' => $processorExtraData] + $this->processorConfig; + } + + /** + * set $f3 base object + */ + public function setF3(){ + $this->f3 = \Base::instance(); + } + + /** + * @param $message + */ + public function setMessage(string $message){ + $this->message = $message; + } + + /** + * @param string $level + * @throws \Exception + */ + public function setLevel(string $level){ + if( in_array($level, self::LEVEL)){ + $this->level = $level; + }else{ + throw new \Exception( sprintf(self::ERROR_LEVEL, $level)); + } + } + + /** + * @param string $tag + * @throws \Exception + */ + public function setTag(string $tag){ + if( in_array($tag, self::TAG)){ + $this->tag = $tag; + }else{ + throw new \Exception( sprintf(self::ERROR_TAG, $tag)); + } + } + + /** + * @param array $data + * @return LogInterface + */ + public function setData(array $data): LogInterface{ + $this->data = $data; + return $this; + } + + /** + * @param array $data + * @return LogInterface + */ + public function setTempData(array $data): LogInterface{ + $this->tmpData = $data; + return $this; + } + + /** + * add new Handler by $handlerKey + * set its default Formatter by $formatterKey + * @param string $handlerKey + * @param string|null $formatterKey + * @param \stdClass|null $handlerParams + * @return LogInterface + */ + public function addHandler(string $handlerKey, string $formatterKey = null, \stdClass $handlerParams = null): LogInterface{ + if(!$this->hasHandlerKey($handlerKey)){ + $this->handlerConfig[$handlerKey] = $formatterKey; + // add more configuration params for the new handler + if(!is_null($handlerParams)){ + $this->handlerParamsConfig[$handlerKey] = $handlerParams; + } + } + return $this; + } + + /** + * add new handler for Log() grouping + * @param string $handlerKey + * @return LogInterface + */ + public function addHandlerGroup(string $handlerKey): LogInterface{ + if( + $this->hasHandlerKey($handlerKey) && + !$this->hasHandlerGroupKey($handlerKey) + ){ + $this->handlerGroups[] = $handlerKey; + } + return $this; + } + + /** + * @return array + */ + public function getHandlerConfig(): array{ + return $this->handlerConfig; + } + + /** + * get __construct() parameters for a given $handlerKey + * @param string $handlerKey + * @return array + * @throws \Exception + */ + public function getHandlerParams(string $handlerKey): array{ + $params = []; + + if($this->hasHandlerKey($handlerKey)){ + switch($handlerKey){ + case 'stream': $params = $this->getHandlerParamsStream(); + break; + case 'zmq': $params = $this->getHandlerParamsZMQ(); + break; + case 'mail': $params = $this->getHandlerParamsMail(); + break; + case 'slackMap': + case 'slackRally': + $params = $this->getHandlerParamsSlack($handlerKey); + break; + default: + throw new \Exception( sprintf(self::ERROR_HANDLER_PARAMS, $handlerKey)); + } + }else{ + throw new \Exception( sprintf(self::ERROR_HANDLER_KEY, $handlerKey, implode(', ', array_flip($this->handlerConfig)))); + } + + return $params; + } + + /** + * @return array + */ + public function getHandlerParamsConfig(): array{ + return $this->handlerParamsConfig; + } + + /** + * @return array + */ + public function getProcessorConfig(): array{ + return $this->processorConfig; + } + + /** + * @return string + */ + public function getMessage(): string{ + return $this->message; + } + + /** + * @return string + */ + public function getAction(): string{ + return $this->action; + } + + /** + * @return string + */ + public function getChannelType(): string{ + return $this->channelType; + } + + /** + * @return string + */ + public function getChannelName(): string{ + return $this->getChannelType(); + } + + /** + * @return string + */ + public function getLevel(): string{ + return $this->level; + } + + /** + * @return string + */ + public function getTag(): string{ + return $this->tag; + } + + /** + * @return array + */ + public function getData(): array{ + return $this->data; + } + /** + * @return array + */ + public function getContext(): array{ + $context = [ + 'data' => $this->getData(), + 'tag' => $this->getTag() + ]; + + // add temp data (e.g. used for $message placeholder replacement + $context += $this->getTempData(); + + return $context; + } + + /** + * @return array + */ + protected function getTempData(): array { + return $this->tmpData; + } + + /** + * @return array + */ + public function getHandlerGroups(): array{ + return $this->handlerGroups; + } + + /** + * get unique hash for this kind of logs (channel) and same $handlerGroups + * @return string + */ + public function getGroupHash(): string { + $groupName = $this->getChannelName(); + if($this->isGrouped()){ + $groupName .= '_' . implode('_', $this->getHandlerGroups()); + } + + return $this->f3->hash($groupName); + } + + /** + * @param string $handlerKey + * @return bool + */ + public function hasHandlerKey(string $handlerKey): bool{ + return array_key_exists($handlerKey, $this->handlerConfig); + } + + /** + * @param string $handlerKey + * @return bool + */ + public function hasHandlerGroupKey(string $handlerKey): bool{ + return in_array($handlerKey, $this->getHandlerGroups()); + } + + /** + * @return bool + */ + public function hasBuffer(): bool{ + return $this->buffer; + } + + /** + * @return bool + */ + public function isGrouped(): bool{ + return !empty($this->getHandlerGroups()); + } + + /** + * remove all group handlers and their config params + */ + public function removeHandlerGroups(){ + foreach($this->getHandlerGroups() as $handlerKey){ + $this->removeHandlerGroup($handlerKey); + } + } + + /** + * @param string $handlerKey + */ + public function removeHandlerGroup(string $handlerKey){ + unset($this->handlerConfig[$handlerKey]); + unset($this->handlerParamsConfig[$handlerKey]); + } + + // Handler parameters for Monolog\Handler\AbstractHandler --------------------------------------------------------- + protected function getHandlerParamsStream(): array{ + $params = []; + if( !empty($conf = $this->handlerParamsConfig['stream']) ){ + $params[] = $conf->stream; + } + + return $params; + } + + /** + * get __construct() parameters for ZMQHandler() call + * @return array + */ + protected function getHandlerParamsZMQ(): array { + $params = []; + if( !empty($conf = $this->handlerParamsConfig['zmq']) ){ + // meta data (required by receiver socket) + $meta = [ + 'logType' => 'mapLog', + 'stream'=> $conf->streamConf->stream + ]; + + $context = new \ZMQContext(); + $pusher = $context->getSocket(\ZMQ::SOCKET_PUSH); + $pusher->connect($conf->uri); + + $params[] = $pusher; + $params[] = \ZMQ::MODE_DONTWAIT; + $params[] = false; // multipart + $params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled + $params[] = true; // bubble + $params[] = $meta; + } + + return $params; + } + + /** + * get __construct() parameters for SwiftMailerHandler() call + * @return array + */ + protected function getHandlerParamsMail(): array{ + $params = []; + if( !empty($conf = $this->handlerParamsConfig['mail']) ){ + $transport = (new \Swift_SmtpTransport()) + ->setHost($conf->host) + ->setPort($conf->port) + ->setEncryption($conf->scheme) + ->setUsername($conf->username) + ->setPassword($conf->password) + ->setStreamOptions([ + 'ssl' => [ + 'allow_self_signed' => true, + 'verify_peer' => false + ] + ]); + + $mailer = new \Swift_Mailer($transport); + + // callback function used instead of Swift_Message() object + // -> we want the formatted/replaced message as subject + $messageCallback = function($content, $records) use ($conf){ + $subject = 'No Subject'; + if(!empty($records)){ + // build subject from first record -> remove "markdown" + $subject = str_replace(['*', '_'], '', $records[0]['message']); + } + + $jsonData = @json_encode($records, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + + $message = (new \Swift_Message()) + ->setSubject($subject) + ->addPart($jsonData) + ->setFrom($conf->from) + ->setTo($conf->to) + ->setContentType('text/html') + ->setCharset('utf-8') + ->setMaxLineLength(1000); + + if($conf->addJson){ + $jsonAttachment = (new \Swift_Attachment()) + ->setFilename('data.json') + ->setContentType('application/json') + ->setBody($jsonData); + $message->attach($jsonAttachment); + } + + return $message; + }; + + $params[] = $mailer; + $params[] = $messageCallback; + $params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled + $params[] = true; // bubble + } + + return $params; + } + + /** + * get __construct() params for SlackWebhookHandler() call + * @param string $handlerKey + * @return array + */ + protected function getHandlerParamsSlack(string $handlerKey): array { + $params = []; + if( !empty($conf = $this->handlerParamsConfig[$handlerKey]) ){ + $params[] = $conf->slackWebHookURL; + $params[] = $conf->slackChannel; + $params[] = $conf->slackUsername; + $params[] = true; // $useAttachment + $params[] = $conf->slackIcon; + $params[] = true; // $includeContext + $params[] = false; // $includeExtra + $params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled + $params[] = true; // $bubble + //$params[] = ['extra', 'context.tag']; // $excludeFields + $params[] = []; // $excludeFields + } + + return $params; + } + + /** + * send this Log to global log buffer storage + */ + public function buffer(){ + Monolog::instance()->push($this); + } + + +} \ No newline at end of file diff --git a/app/main/lib/logging/DefaultLog.php b/app/main/lib/logging/DefaultLog.php new file mode 100644 index 00000000..5b0fb122 --- /dev/null +++ b/app/main/lib/logging/DefaultLog.php @@ -0,0 +1,19 @@ + no default is set + * @var array + */ + protected $handlerConfig = []; + + /** + * processors for this collection + * -> no default is set + * @var array + */ + protected $processorConfig = []; + + /** + * @var null|\SplObjectStorage + */ + private $collection = null; + + public function __construct(string $action){ + parent::__construct($action); + + $this->collection = new \SplObjectStorage(); + } + + /** + * get first Log from Collection + * @return AbstractLog + * @throws \Exception + */ + protected function getPrimaryLog(): AbstractLog{ + $this->collection->rewind(); + if($this->collection->valid()){ + /** + * @var $log AbstractLog + */ + $log = $this->collection->current(); + }else{ + throw new \Exception( self::ERROR_EMPTY); + } + + return $log; + } + + /** + * add a new log object to this collection + * @param AbstractLog $log + */ + public function addLog(AbstractLog $log){ + if(!$this->collection->contains($log)){ + if(!$this->collection->count()){ + // first log sets the default for this collection + $this->channelType = $log->getChannelType(); + + // get relevant handlerKeys for this collection + $handlerGroups = array_flip($log->getHandlerGroups()); + + // remove handlers that are not relevant for this collection + $handlerConfig = $log->getHandlerConfig(); + $handlerConfigGroup = array_intersect_key($handlerConfig, $handlerGroups); + + // remove handlersParams that are not relevant for this collection + $handlerParamsConfig = $log->getHandlerParamsConfig(); + $handlerParamsConfigGroup = array_intersect_key($handlerParamsConfig, $handlerGroups); + + // add all handlers that are relevant for this collection + foreach($handlerConfigGroup as $handlerKey => $formatterKey){ + $handlerParams = array_key_exists($handlerKey, $handlerParamsConfigGroup) ? $handlerParamsConfigGroup[$handlerKey] : null; + $this->addHandler($handlerKey, $formatterKey, $handlerParams); + } + + // add processors for this collection + $this->processorConfig = $log->getProcessorConfig(); + } + + $this->setMessage($log->getMessage()); + $this->setTag($log->getTag()); + + $this->collection->attach($log); + } + } + + /** + * @param string $message + */ + public function setMessage(string $message){ + $currentMessage = parent::getMessage(); + if(empty($currentMessage)){ + $newMessage = $message; + }elseif($message !== $currentMessage){ + $newMessage = 'multi changes'; + }else{ + $newMessage = $currentMessage ; + } + + parent::setMessage($newMessage); + } + + /** + * @param string $tag + */ + public function setTag(string $tag){ + $currentTag = parent::getTag(); + switch($currentTag){ + case 'default': + // no specific tag set so far... set new + $newTag = $tag; break; + case 'information': + // do not change "information" tag (mixed tag logs in this collection) + $newTag = $currentTag; break; + default: + // set mixed tag -> "information" + $newTag = ($tag !== $currentTag) ? 'information': $tag; + } + + parent::setTag($newTag); + } + + /** + * get log data for all logs in this collection + * @return array + */ + public function getData() : array{ + $this->collection->rewind(); + $data = []; + while($this->collection->valid()){ + $data[] = $this->collection->current()->getData(); + $this->collection->next(); + } + return $data; + } + + /** + * @return string + */ + public function getChannelName() : string{ + return $this->getPrimaryLog()->getChannelName(); + } + + /** + * @return string + */ + public function getLevel() : string{ + return $this->getPrimaryLog()->getLevel(); + } + + /** + * @return bool + */ + public function hasBuffer() : bool{ + return $this->getPrimaryLog()->hasBuffer(); + } + + /** + * @return array + */ + public function getTempData() : array{ + return $this->getPrimaryLog()->getTempData(); + } + + + +} \ No newline at end of file diff --git a/app/main/lib/logging/LogInterface.php b/app/main/lib/logging/LogInterface.php new file mode 100644 index 00000000..3845b892 --- /dev/null +++ b/app/main/lib/logging/LogInterface.php @@ -0,0 +1,64 @@ + final handler will be set dynamic for per instance + * @var array + */ + protected $handlerConfig = [ + //'stream' => 'json', + //'zmq' => 'json', + //'slackMap' => 'json' + ]; + + /** + * @var string + */ + protected $channelType = 'map'; + + /** + * @var bool + */ + protected $logActivity = false; + + + public function __construct(string $action, array $objectData){ + parent::__construct($action, $objectData); + + $this->setLevel('info'); + $this->setTag($this->getTagFromAction()); + } + + /** + * get log tag depending on log action + * @return string + */ + public function getTagFromAction(){ + $tag = parent::getTag(); + $actionParts = $this->getActionParts(); + switch($actionParts[1]){ + case 'create': $tag = 'success'; break; + case 'update': $tag = 'warning'; break; + case 'delete': $tag = 'danger'; break; + } + + return $tag; + } + + /** + * @return string + */ + public function getChannelName(): string{ + return $this->getChannelType() . '_' . $this->getChannelId(); + } + + /** + * @return string + */ + public function getMessage() : string{ + return $this->getActionParts()[0] . " '{objName}'"; + } + + /** + * @return array + */ + public function getData() : array{ + $data = parent::getData(); + + // add system, connection, signature data ------------------------------------------------- + if(!empty($tempLogData = $this->getTempData())){ + $objectData['object'] = $tempLogData; + $data = $objectData + $data; + } + + // add human readable changes to string --------------------------------------------------- + $data['formatted'] = $this->formatData($data); + + return $data; + } + + /** + * @param array $data + * @return string + */ + protected function formatData(array $data): string{ + $actionParts = $this->getActionParts(); + $objectString = !empty($data['object']) ? "'" . $data['object']['objName'] . "'" . ' #' . $data['object']['objId'] : ''; + $string = ucfirst($actionParts[1]) . 'd ' . $actionParts[0] . " " . $objectString; + + // format changed columns (recursive) --------------------------------------------- + switch($actionParts[1]){ + case 'create': + case 'update': + $formatChanges = function(array $changes) use ( &$formatChanges ): string{ + $string = ''; + foreach($changes as $field => $value){ + if(is_array($value)){ + $string .= $field . ": "; + $string .= $formatChanges($value); + $string .= next( $changes ) ? " , " : ''; + }else{ + if(is_numeric($value)){ + $formattedValue = $value; + }elseif(is_null($value)){ + $formattedValue = "NULL"; + }elseif(empty($value)){ + $formattedValue = "' '"; + }elseif(is_string($value)){ + $formattedValue = "'" . $value . "'"; + }else{ + $formattedValue = (string)$value; + } + + $string .= $formattedValue; + if($field == 'old'){ + $string .= " ➜ "; + } + } + } + return $string; + }; + + $string .= ' | ' . $formatChanges($data['main']); + break; + } + + return $string; + } + + /** + * split $action "CamelCase" wise + * @return array + */ + protected function getActionParts(): array{ + return array_map('strtolower', preg_split('/(?=[A-Z])/', $this->getAction())); + } + + /** + * @param bool $logActivity + */ + public function logActivity(bool $logActivity){ + $this->logActivity = $logActivity; + } + + public function buffer(){ + parent::buffer(); + + if($this->logActivity){ + // map logs should also used for "activity" logging + LogController::instance()->push($this); + } + } + +} \ No newline at end of file diff --git a/app/main/lib/logging/RallyLog.php b/app/main/lib/logging/RallyLog.php new file mode 100644 index 00000000..ea52e9c8 --- /dev/null +++ b/app/main/lib/logging/RallyLog.php @@ -0,0 +1,105 @@ + final handler will be set dynamic for per instance + * @var array + */ + protected $handlerConfig = [ + // 'slackRally' => 'json', + // 'mail' => 'html' + ]; + + /** + * @var string + */ + protected $channelType = 'rally'; + + + public function __construct(string $action, array $objectData){ + parent::__construct($action, $objectData); + + $this->setLevel('notice'); + $this->setTag('information'); + } + + /** + * @return string + */ + protected function getThumbUrl() : string{ + $url = ''; + if(is_object($character = $this->getCharacter())){ + $characterLog = $character->getLog(); + if($characterLog && !empty($characterLog->shipTypeId)){ + $url = Config::getPathfinderData('api.ccp_image_server') . '/Render/' . $characterLog->shipTypeId . '_64.png'; + }else{ + $url = parent::getThumbUrl(); + } + } + + return $url; + } + + /** + * @return string + */ + public function getMessage() : string{ + return "*New RallyPoint system '{objName}'* _#{objId}_ *map '{channelName}'* _#{channelId}_ "; + } + + /** + * @return array + */ + public function getData() : array{ + $data = parent::getData(); + + // add system ----------------------------------------------------------------------------- + if(!empty($tempLogData = $this->getTempData())){ + $objectData['object'] = $tempLogData; + $data = $objectData + $data; + } + + // add human readable changes to string --------------------------------------------------- + $data['formatted'] =$this->formatData($data); + + return $data; + } + + /** + * @param array $data + * @return string + */ + protected function formatData(array $data): string{ + $string = ''; + + if( + !empty($data['object']) && + !empty($data['channel']) + ){ + $replace = [ + '{objName}' => $data['object']['objName'], + '{objId}' => $data['object']['objId'], + '{channelName}' => $data['channel']['channelName'], + '{channelId}' => $data['channel']['channelId'] + ]; + $string = str_replace(array_keys($replace), array_values($replace), $this->getMessage()); + } + + return $string; + } + + + +} \ No newline at end of file diff --git a/app/main/lib/logging/UserLog.php b/app/main/lib/logging/UserLog.php new file mode 100644 index 00000000..3c6e8ba7 --- /dev/null +++ b/app/main/lib/logging/UserLog.php @@ -0,0 +1,36 @@ + final handler will be set dynamic for per instance + * @var array + */ + protected $handlerConfig = [ + // 'mail' => 'html' + ]; + + /** + * @var string + */ + protected $channelType = 'user'; + + public function __construct(string $action, array $objectData){ + parent::__construct($action, $objectData); + + $this->setLevel('notice'); + $this->setTag('information'); + } + + +} \ No newline at end of file diff --git a/app/main/lib/logging/formatter/MailFormatter.php b/app/main/lib/logging/formatter/MailFormatter.php new file mode 100644 index 00000000..cc980035 --- /dev/null +++ b/app/main/lib/logging/formatter/MailFormatter.php @@ -0,0 +1,46 @@ + $record['message'], + 'tplGreeting' => \Markdown::instance()->convert(str_replace('*', '', $record['message'])), + 'message' => false, + 'tplText2' => false, + 'tplClosing' => 'Fly save!', + 'actionPrimary' => false, + 'appName' => Config::getPathfinderData('name'), + 'appUrl' => Config::getEnvironmentData('URL'), + 'appHost' => $_SERVER['HTTP_HOST'], + 'appContact' => Config::getPathfinderData('contact'), + 'appMail' => Config::getPathfinderData('email'), + ]; + + $tplData = array_replace_recursive($tplDefaultData, (array)$record['context']['data']['main']); + + return \Template::instance()->render('templates/mail/basic_inline.html', 'text/html', $tplData); + } + + public function formatBatch(array $records){ + $message = ''; + foreach ($records as $key => $record) { + $message .= $this->format($record); + } + + return $message; + } + +} \ No newline at end of file diff --git a/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php b/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php new file mode 100644 index 00000000..ab774ea8 --- /dev/null +++ b/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php @@ -0,0 +1,260 @@ +webhookUrl = $webhookUrl; + $this->channel = $channel; + $this->username = $username; + $this->userIcon = trim($iconEmoji, ':'); + $this->useAttachment = $useAttachment; + $this->includeContext = $includeContext; + $this->includeExtra = $includeExtra; + $this->excludeFields = $excludeFields; + + parent::__construct($level, $bubble); + + } + + /** + * format + * @param array $record + * @return array + */ + protected function getSlackData(array $record): array { + $postData = []; + + if ($this->username) { + $postData['username'] = $this->username; + } + + if ($this->channel) { + $postData['channel'] = $this->channel; + } + + $postData['text'] = (string)$record['message']; + + if ($this->userIcon) { + if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { + $postData['icon_url'] = $this->userIcon; + } else { + $postData['icon_emoji'] = ":{$this->userIcon}:"; + } + } + + return $postData; + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record){ + $record = $this->excludeFields($record); + + $postData = $this->getSlackData($record); + + $postData = $this->cleanAttachments($postData); + + $postString = json_encode($postData); + + $ch = curl_init(); + $options = [ + CURLOPT_URL => $this->webhookUrl, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => ['Content-type: application/json'], + CURLOPT_POSTFIELDS => $postString + ]; + if (defined('CURLOPT_SAFE_UPLOAD')) { + $options[CURLOPT_SAFE_UPLOAD] = true; + } + + curl_setopt_array($ch, $options); + + Handler\Curl\Util::execute($ch); + } + + /** + * @param array $postData + * @return array + */ + protected function cleanAttachments(array $postData): array{ + $attachmentCount = count($postData['attachments']); + if( $attachmentCount > $this->maxAttachments){ + $text = 'To many attachments! ' . ($attachmentCount - $this->maxAttachments) . ' of ' . $attachmentCount . ' attachments not visible'; + $postData['attachments'] = array_slice($postData['attachments'], 0, $this->maxAttachments); + + $attachment = [ + 'title' => $text, + 'fallback' => $text, + 'color' => $this->getAttachmentColor('information') + ]; + + $postData['attachments'][] = $attachment; + } + + return $postData; + } + + /** + * @param array $attachment + * @param array $characterData + * @return array + */ + protected function setAuthor(array $attachment, array $characterData): array { + if( !empty($characterData['id']) && !empty($characterData['name'])){ + $attachment['author_name'] = $characterData['name'] . ' #' . $characterData['id']; + $attachment['author_link'] = Config::getPathfinderData('api.z_killboard') . '/character/' . $characterData['id'] . '/'; + $attachment['author_icon'] = Config::getPathfinderData('api.ccp_image_server') . '/Character/' . $characterData['id'] . '_32.jpg'; + } + + return $attachment; + } + + /** + * @param array $attachment + * @param array $thumbData + * @return array + */ + protected function setThumb(array $attachment, array $thumbData): array { + if( !empty($thumbData['url'])) { + $attachment['thumb_url'] = $thumbData['url']; + } + + return $attachment; + } + + /** + * @param $title + * @param $value + * @param bool $format + * @param bool $short + * @return array + */ + protected function generateAttachmentField($title, $value, $format = false, $short = true){ + return [ + 'title' => $title, + 'value' => !empty($value) ? ( $format ? sprintf('`%s`', $value) : $value ) : '', + 'short' => $short + ]; + } + + /** + * @param string $tag + * @return string + */ + protected function getAttachmentColor(string $tag): string { + switch($tag){ + case 'information': $color = '#428bca'; break; + case 'success': $color = '#4f9e4f'; break; + case 'warning': $color = '#e28a0d'; break; + case 'danger': $color = '#a52521'; break; + default: $color = '#313335'; break; + } + return $color; + } + + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * @param array $record + * @return array + */ + private function excludeFields(array $record){ + foreach($this->excludeFields as $field){ + $keys = explode('.', $field); + $node = &$record; + $lastKey = end($keys); + foreach($keys as $key){ + if(!isset($node[$key])){ + break; + } + if($lastKey === $key){ + unset($node[$key]); + break; + } + $node = &$node[$key]; + } + } + + return $record; + } +} \ No newline at end of file diff --git a/app/main/lib/logging/handler/SlackMapWebhookHandler.php b/app/main/lib/logging/handler/SlackMapWebhookHandler.php new file mode 100644 index 00000000..d942c754 --- /dev/null +++ b/app/main/lib/logging/handler/SlackMapWebhookHandler.php @@ -0,0 +1,101 @@ +getTimestamp(); + $text = ''; + + if ( + $this->useAttachment && + !empty( $attachmentsData = $record['context']['data']) + ) { + + // convert non grouped data (associative array) to multi dimensional (sequential) array + // -> see "group" records + $attachmentsData = Util::is_assoc($attachmentsData) ? [$attachmentsData] : $attachmentsData; + + $thumbData = (array)$record['extra']['thumb']; + + $postData['attachments'] = []; + + foreach($attachmentsData as $attachmentData){ + $channelData = (array)$attachmentData['channel']; + $characterData = (array)$attachmentData['character']; + $formatted = (string)$attachmentData['formatted']; + + // get "message" from $formatted + $msgParts = explode('|', $formatted, 2); + + // build main text from first Attachment (they belong to same channel) + if(!empty($channelData)){ + $text = "*Map '" . $channelData['channelName'] . "'* _#" . $channelData['channelId'] . "_ *changed*"; + } + + $attachment = [ + 'title' => !empty($msgParts[0]) ? $msgParts[0] : 'No Title', + //'pretext' => '', + 'text' => !empty($msgParts[1]) ? sprintf('```%s```', $msgParts[1]) : '', + 'fallback' => !empty($msgParts[1]) ? $msgParts[1] : 'No Fallback', + 'color' => $this->getAttachmentColor($tag), + 'fields' => [], + 'mrkdwn_in' => ['fields', 'text'], + 'footer' => 'Pathfinder API', + //'footer_icon'=> '', + 'ts' => $timestamp + ]; + + $attachment = $this->setAuthor($attachment, $characterData); + $attachment = $this->setThumb($attachment, $thumbData); + + + // set 'field' array ---------------------------------------------------------------------------------- + if ($this->includeExtra) { + $attachment['fields'][] = $this->generateAttachmentField('', 'Meta data:', false, false); + + if(!empty($record['extra']['path'])){ + $attachment['fields'][] = $this->generateAttachmentField('Path', $record['extra']['path'], true); + } + + if(!empty($tag)){ + $attachment['fields'][] = $this->generateAttachmentField('Tag', $tag, true); + } + + if(!empty($record['level_name'])){ + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name'], true); + } + + if(!empty($record['extra']['ip'])){ + $attachment['fields'][] = $this->generateAttachmentField('IP', $record['extra']['ip'], true); + } + } + + $postData['attachments'][] = $attachment; + } + } + + $postData['text'] = empty($text) ? $postData['text'] : $text; + + + return $postData; + } + + +} \ No newline at end of file diff --git a/app/main/lib/logging/handler/SlackRallyWebhookHandler.php b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php new file mode 100644 index 00000000..f85f120c --- /dev/null +++ b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php @@ -0,0 +1,135 @@ +getTimestamp(); + $text = ''; + + if ( + $this->useAttachment && + !empty( $attachmentsData = $record['context']['data']) + ){ + // convert non grouped data (associative array) to multi dimensional (sequential) array + // -> see "group" records + $attachmentsData = Util::is_assoc($attachmentsData) ? [$attachmentsData] : $attachmentsData; + + $thumbData = (array)$record['extra']['thumb']; + + $postData['attachments'] = []; + + foreach($attachmentsData as $attachmentData){ + $characterData = (array)$attachmentData['character']; + + $text = 'No Title'; + if( !empty($attachmentData['formatted']) ){ + $text = $attachmentData['formatted']; + } + + $attachment = [ + 'title' => !empty($attachmentData['main']['message']) ? 'Message' : '', + //'pretext' => '', + 'text' => !empty($attachmentData['main']['message']) ? sprintf('```%s```', $attachmentData['main']['message']) : '', + 'fallback' => !empty($attachmentData['main']['message']) ? $attachmentData['main']['message'] : 'No Fallback', + 'color' => $this->getAttachmentColor($tag), + 'fields' => [], + 'mrkdwn_in' => ['fields', 'text'], + 'footer' => 'Pathfinder API', + //'footer_icon'=> '', + 'ts' => $timestamp + ]; + + $attachment = $this->setAuthor($attachment, $characterData); + $attachment = $this->setThumb($attachment, $thumbData); + + // set 'field' array ---------------------------------------------------------------------------------- + if ($this->includeContext) { + if(!empty($objectData = $attachmentData['object'])){ + if(!empty($objectData['objAlias'])){ + // System alias + $attachment['fields'][] = $this->generateAttachmentField('Alias', $objectData['objAlias']); + } + + if(!empty($objectData['objName'])){ + // System name + $attachment['fields'][] = $this->generateAttachmentField('System', $objectData['objName']); + } + + if(!empty($objectData['objRegion'])){ + // System region + $attachment['fields'][] = $this->generateAttachmentField('Region', $objectData['objRegion']); + } + + if(isset($objectData['objIsWormhole'])){ + // Is wormhole + $attachment['fields'][] = $this->generateAttachmentField('Wormhole', $objectData['objIsWormhole'] ? 'Yes' : 'No'); + } + + if(!empty($objectData['objSecurity'])){ + // System security + $attachment['fields'][] = $this->generateAttachmentField('Security', $objectData['objSecurity']); + } + + if(!empty($objectData['objEffect'])){ + // System effect + $attachment['fields'][] = $this->generateAttachmentField('Effect', $objectData['objEffect']); + } + + if(!empty($objectData['objTrueSec'])){ + // System trueSec + $attachment['fields'][] = $this->generateAttachmentField('TrueSec', $objectData['objTrueSec']); + } + + if(!empty($objectData['objDescription'])){ + // System trueSec + $attachment['fields'][] = $this->generateAttachmentField('System description', '```' . $objectData['objDescription'] . '```', false, false); + } + } + } + + if($this->includeExtra){ + if(!empty($record['extra']['path'])){ + $attachment['fields'][] = $this->generateAttachmentField('Path', $record['extra']['path'], true); + } + + if(!empty($tag)){ + $attachment['fields'][] = $this->generateAttachmentField('Tag', $tag, true); + } + + if(!empty($record['level_name'])){ + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name'], true); + } + + if(!empty($record['extra']['ip'])){ + $attachment['fields'][] = $this->generateAttachmentField('IP', $record['extra']['ip'], true); + } + } + + $postData['attachments'][] = $attachment; + } + } + + $postData['text'] = empty($text) ? $postData['text'] : $text; + + return $postData; + } + + +} \ No newline at end of file diff --git a/app/main/lib/logging/handler/ZMQHandler.php b/app/main/lib/logging/handler/ZMQHandler.php new file mode 100644 index 00000000..f049414c --- /dev/null +++ b/app/main/lib/logging/handler/ZMQHandler.php @@ -0,0 +1,65 @@ +metaData = $metaData; + + parent::__construct($zmqSocket, $zmqMode, $multipart, $level, $bubble); + } + + /** + * overwrite default handle() + * -> change data structure after processor() calls and before formatter() calls + * @param array $record + * @return bool + * @throws \Exception + */ + public function handle(array $record){ + if (!$this->isHandling($record)) { + return false; + } + + $record = $this->processRecord($record); + + $record = [ + 'task' => 'logData', + 'load' => [ + 'meta' => $this->metaData, + 'log' => $record + ] + ]; + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + +} \ No newline at end of file diff --git a/app/main/lib/socket.php b/app/main/lib/socket.php index fb96c108..98cc7e8e 100644 --- a/app/main/lib/socket.php +++ b/app/main/lib/socket.php @@ -79,27 +79,19 @@ class Socket { /** * init new socket */ - /* public function initSocket(){ - if(self::checkRequirements()){ - $context = new \ZMQContext(); - $this->socket = $context->getSocket(\ZMQ::SOCKET_REQ); - // The linger value of the socket. Specifies how long the socket blocks trying flush messages after it has been closed - $this->socket->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0); - } - } */ - - /** - * init new socket - */ - public function initSocket(){ - if(self::checkRequirements()){ + if(Config::checkSocketRequirements()){ $context = new \ZMQContext(); $this->socket = $context->getSocket(\ZMQ::SOCKET_PUSH); } } - public function sendData($task, $load = ''){ + /** + * @param $task + * @param string $load + * @return bool|string + */ + public function sendData(string $task, $load = ''){ $response = false; $this->initSocket(); @@ -122,7 +114,7 @@ class Socket { //$this->socket->send(json_encode($send), \ZMQ::MODE_DONTWAIT); $this->socket->send(json_encode($send)); - $this->socket->disconnect($this->socketUri); + // $this->socket->disconnect($this->socketUri); $response = 'OK'; @@ -230,24 +222,5 @@ class Socket { return $response; }*/ - /** - * check whether this installation fulfills all requirements - * -> check for ZMQ PHP extension and installed ZQM version - * -> this does NOT check versions! -> those can be verified on /setup page - * @return bool - */ - static function checkRequirements(){ - $check = false; - - if( - extension_loaded('zmq') && - class_exists('ZMQ') - ){ - $check = true; - } - - return $check; - } - } \ No newline at end of file diff --git a/app/main/lib/util.php b/app/main/lib/util.php index 1001cf99..cf4e2c19 100644 --- a/app/main/lib/util.php +++ b/app/main/lib/util.php @@ -17,11 +17,32 @@ class Util { * @return array */ static function arrayChangeKeyCaseRecursive($arr, $case = CASE_LOWER){ - return array_map( function($item){ - if( is_array($item) ) - $item = self::arrayChangeKeyCaseRecursive($item); - return $item; - }, array_change_key_case($arr, $case)); + if(is_array($arr)){ + $arr = array_map( function($item){ + if( is_array($item) ) + $item = self::arrayChangeKeyCaseRecursive($item); + return $item; + }, array_change_key_case((array)$arr, $case)); + } + + return $arr; + } + + /** + * checks whether an array is associative or not (sequential) + * @param mixed $array + * @return bool + */ + static function is_assoc($array): bool { + $isAssoc = false; + if( + is_array($array) && + array_keys($array) !== range(0, count($array) - 1) + ){ + $isAssoc = true; + } + + return $isAssoc; } /** @@ -59,6 +80,23 @@ class Util { return $scopes; } + /** + * obsucre string e.g. password (hide last characters) + * @param string $string + * @param int $maxHideChars + * @return string + */ + static function obscureString(string $string, int $maxHideChars = 10): string { + $formatted = ''; + $length = mb_strlen((string)$string); + if($length > 0){ + $hideChars = ($length < $maxHideChars) ? $length : $maxHideChars; + $formatted = substr_replace($string, str_repeat('_', min(3, $length)), -$hideChars) . + ' [' . $length . ']'; + } + return $formatted; + } + /** * get hash from an array of ESI scopes * @param array $scopes diff --git a/app/main/model/abstractmaptrackingmodel.php b/app/main/model/abstractmaptrackingmodel.php new file mode 100644 index 00000000..5098eef4 --- /dev/null +++ b/app/main/model/abstractmaptrackingmodel.php @@ -0,0 +1,139 @@ + [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\CharacterModel', + 'constraint' => [ + [ + 'table' => 'character', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'validate_notDry' + ], + 'updatedCharacterId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\CharacterModel', + 'constraint' => [ + [ + 'table' => 'character', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'validate_notDry' + ] + ]; + + /** + * get static character fields for this model instance + * @return array + */ + protected function getStaticFieldConf(): array{ + return array_merge(parent::getStaticFieldConf(), $this->trackingFieldConf); + } + + /** + * validates a model field to be a valid relational model + * @param $key + * @param $val + * @return bool + */ + protected function validate_notDry($key, $val): bool { + $valid = true; + if($colConf = $this->fieldConf[$key]){ + if(isset($colConf['belongs-to-one'])){ + if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){ + $valid = true; + }elseif( is_a($val, $colConf['belongs-to-one']) && !$val->dry() ){ + $valid = true; + }else{ + $valid = false; + $msg = 'Validation failed: "' . get_class($this) . '->' . $key . '" must be a valid instance of ' . $colConf['belongs-to-one']; + $this->throwValidationException($key, $msg); + } + } + } + + return $valid; + } + + /** + * log character activity create/update/delete events + * @param string $action + */ + protected function logActivity($action){ + // check if activity logging is enabled for this object + if($this->enableActivityLogging){ + // check for field changes + if( + mb_stripos(mb_strtolower($action), 'delete') !== false || + !empty($this->fieldChanges) + ){ + $this->newLog($action)->setCharacter($this->updatedCharacterId)->setData($this->fieldChanges)->buffer(); + } + } + } + + /** + * validates all required columns of this class + * @return bool + * @throws \Exception\ValidationException + */ + public function isValid(): bool { + if($valid = parent::isValid()){ + foreach($this->trackingFieldConf as $key => $colConf){ + if($this->exists($key)){ + $valid = $this->validateField($key, $this->$key); + if(!$valid){ + break; + } + }else{ + $valid = false; + $this->throwDbException('Missing table column "' . $this->getTable(). '.' . $key . '"'); + break; + } + } + } + + return $valid; + } + + /** + * get log file data + * @return array + */ + public function getLogData(): array { + return []; + } + + + /** + * save connection + * @param CharacterModel $characterModel + * @return ConnectionModel|false + */ + public function save(CharacterModel $characterModel = null){ + if($this->dry()){ + $this->createdCharacterId = $characterModel; + } + $this->updatedCharacterId = $characterModel; + + return parent::save(); + } + +} \ No newline at end of file diff --git a/app/main/model/activitylogmodel.php b/app/main/model/activitylogmodel.php index 19a32b79..bcee4e71 100644 --- a/app/main/model/activitylogmodel.php +++ b/app/main/model/activitylogmodel.php @@ -44,22 +44,46 @@ class ActivityLogModel extends BasicModel { ] ], + // map actions ----------------------------------------------------- + + 'mapCreate' => [ + 'type' => Schema::DT_SMALLINT, + 'nullable' => false, + 'default' => 0, + 'counter' => true + ], + 'mapUpdate' => [ + 'type' => Schema::DT_SMALLINT, + 'nullable' => false, + 'default' => 0, + 'counter' => true + ], + 'mapDelete' => [ + 'type' => Schema::DT_SMALLINT, + 'nullable' => false, + 'default' => 0, + 'counter' => true + ], + // system actions ----------------------------------------------------- 'systemCreate' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'systemUpdate' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'systemDelete' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], // connection actions ------------------------------------------------- @@ -68,16 +92,19 @@ class ActivityLogModel extends BasicModel { 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'connectionUpdate' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'connectionDelete' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], // signature actions ------------------------------------------------- @@ -86,16 +113,19 @@ class ActivityLogModel extends BasicModel { 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'signatureUpdate' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], 'signatureDelete' => [ 'type' => Schema::DT_SMALLINT, 'nullable' => false, 'default' => 0, + 'counter' => true ], ]; @@ -128,6 +158,20 @@ class ActivityLogModel extends BasicModel { } } + /** + * get all table columns that are used as "counter" columns + * @return array + */ + public function getCountableColumnNames(): array { + $fieldConf = $this->getFieldConfiguration(); + + $filterCounterColumns = function($key, $value){ + return isset($value['counter']) ? $key : false; + }; + + return array_values(array_filter(array_map($filterCounterColumns, array_keys($fieldConf), $fieldConf))); + } + /** * overwrites parent * @param null $db diff --git a/app/main/model/alliancemodel.php b/app/main/model/alliancemodel.php index d5bca132..23bcb703 100644 --- a/app/main/model/alliancemodel.php +++ b/app/main/model/alliancemodel.php @@ -9,6 +9,7 @@ namespace Model; use DB\SQL\Schema; +use lib\Config; class AllianceModel extends BasicModel { @@ -60,8 +61,6 @@ class AllianceModel extends BasicModel { public function getMaps(){ $maps = []; - $f3 = self::getF3(); - $this->filter('mapAlliances', ['active = ?', 1], ['order' => 'created'] @@ -72,7 +71,7 @@ class AllianceModel extends BasicModel { foreach($this->mapAlliances as $mapAlliance){ if( $mapAlliance->mapId->isActive() && - $mapCount < $f3->get('PATHFINDER.MAP.ALLIANCE.MAX_COUNT') + $mapCount < Config::getMapsDefaultConfig('alliance')['max_count'] ){ $maps[] = $mapAlliance->mapId; $mapCount++; diff --git a/app/main/model/basicmodel.php b/app/main/model/basicmodel.php index 272080f7..bb0e7e96 100644 --- a/app/main/model/basicmodel.php +++ b/app/main/model/basicmodel.php @@ -9,9 +9,11 @@ namespace Model; use DB\SQL\Schema; -use Exception; use Controller; use DB; +use Lib\Logging; +use Exception\ValidationException; +use Exception\DatabaseException; abstract class BasicModel extends \DB\Cortex { @@ -19,7 +21,7 @@ abstract class BasicModel extends \DB\Cortex { * Hive key with DB object * @var string */ - protected $db = 'DB_PF'; + protected $db = 'DB_PF'; /** * caching time of field schema - seconds @@ -27,26 +29,20 @@ abstract class BasicModel extends \DB\Cortex { * -> leave this at a higher value * @var int */ - protected $ttl = 120; + protected $ttl = 60; /** * caching for relational data * @var int */ - protected $rel_ttl = 0; + protected $rel_ttl = 0; /** * ass static columns for this table * -> can be overwritten in child models * @var bool */ - protected $addStaticFields = true; - - /** - * field validation array - * @var array - */ - protected $validate = []; + protected $addStaticFields = true; /** * enables check for $fieldChanges on update/insert @@ -54,7 +50,7 @@ abstract class BasicModel extends \DB\Cortex { * in $fieldConf config * @var bool */ - protected $enableActivityLogging = true; + protected $enableActivityLogging = true; /** * enables change for "active" column @@ -62,40 +58,53 @@ abstract class BasicModel extends \DB\Cortex { * -> $this->active = false; will NOT work (prevent abuse)! * @var bool */ - private $allowActiveChange = false; + private $allowActiveChange = false; /** * getData() cache key prefix * -> do not change, otherwise cached data is lost * @var string */ - private $dataCacheKeyPrefix = 'DATACACHE'; + private $dataCacheKeyPrefix = 'DATACACHE'; /** * enables data export for this table * -> can be overwritten in child models * @var bool */ - public static $enableDataExport = false; + public static $enableDataExport = false; /** * enables data import for this table * -> can be overwritten in child models * @var bool */ - public static $enableDataImport = false; + public static $enableDataImport = false; /** * changed fields (columns) on update/insert * -> e.g. for character "activity logging" * @var array */ - protected $fieldChanges = []; + protected $fieldChanges = []; /** - * default TTL for getData(); cache + * collection for validation errors + * @var array */ - const DEFAULT_CACHE_TTL = 120; + protected $validationError = []; + + /** + * default caching time of field schema - seconds + */ + const DEFAULT_TTL = 86400; + + /** + * default TTL for getData(); cache - seconds + */ + const DEFAULT_CACHE_TTL = 120; + + const ERROR_INVALID_MODEL_CLASS = 'Model class (%s) not found'; public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){ @@ -135,8 +144,8 @@ abstract class BasicModel extends \DB\Cortex { /** * @param string $key * @param mixed $val - * @return mixed|void - * @throws Exception\ValidationException + * @return mixed + * @throws ValidationException */ public function set($key, $val){ if( @@ -167,15 +176,13 @@ abstract class BasicModel extends \DB\Cortex { $val = trim($val); } - $valid = $this->validateField($key, $val); - - if(!$valid){ - $this->throwValidationError($key); + if( !$this->validateField($key, $val) ){ + $this->throwValidationException($key); }else{ $this->checkFieldForActivityLogging($key, $val); - - return parent::set($key, $val); } + + return parent::set($key, $val); } /** @@ -206,15 +213,25 @@ abstract class BasicModel extends \DB\Cortex { $val = (int)$val; } + if(is_object($val)){ + $val = $val->_id; + } + if( $fieldConf['type'] === self::DT_JSON){ $currentValue = $this->get($key); }else{ $currentValue = $this->get($key, true); } + if($currentValue !== $val){ // field has changed - in_array($key, $this->fieldChanges) ?: $this->fieldChanges[] = $key; + if( !array_key_exists($key, $this->fieldChanges) ){ + $this->fieldChanges[$key] = [ + 'old' => $currentValue, + 'new' => $val + ]; + } } } } @@ -239,11 +256,12 @@ abstract class BasicModel extends \DB\Cortex { } /** - * extent the fieldConf Array with static fields for each table + * get static fields for this model instance + * @return array */ - private function addStaticFieldConfig(){ + protected function getStaticFieldConf(): array { + $staticFieldConfig = []; - // add static fields to this mapper // static tables (fixed data) do not require them... if($this->addStaticFields){ $staticFieldConfig = [ @@ -258,61 +276,37 @@ abstract class BasicModel extends \DB\Cortex { 'index' => true ] ]; - - - $this->fieldConf = array_merge($staticFieldConfig, $this->fieldConf); } + + return $staticFieldConfig; + } + + /** + * extent the fieldConf Array with static fields for each table + */ + private function addStaticFieldConfig(){ + $this->fieldConf = array_merge($this->getStaticFieldConf(), $this->fieldConf); } /** * validates a table column based on validation settings - * @param $col + * @param string $key * @param $val * @return bool */ - private function validateField($col, $val){ + protected function validateField(string $key, $val): bool { $valid = true; - - if(array_key_exists($col, $this->validate)){ - - $fieldValidationOptions = $this->validate[$col]; - - foreach($fieldValidationOptions as $validateKey => $validateOption ){ - if(is_array($fieldValidationOptions[$validateKey])){ - $fieldSubValidationOptions = $fieldValidationOptions[$validateKey]; - - foreach($fieldSubValidationOptions as $validateSubKey => $validateSubOption ){ - switch($validateKey){ - case 'length': - switch($validateSubKey){ - case 'min'; - if(strlen($val) < $validateSubOption){ - $valid = false; - } - break; - case 'max'; - - if(strlen($val) > $validateSubOption){ - $valid = false; - } - break; - } - break; - } - } - + if($fieldConf = $this->fieldConf[$key]){ + if($method = $this->fieldConf[$key]['validate']){ + if( !is_string($method)){ + $method = 'validate_' . $key; + } + if(method_exists($this, $method)){ + // validate $key (column) with this method... + $valid = $this->$method($key, $val); }else{ - switch($validateKey){ - case 'regex': - $valid = (bool)preg_match($fieldValidationOptions[$validateKey], $val); - break; - } - } - - // a validation rule failed - if(!$valid){ - break; - } + self::getF3()->error(501, 'Method ' . get_class($this) . '->' . $method . '() is not implemented'); + }; } } @@ -428,13 +422,22 @@ abstract class BasicModel extends \DB\Cortex { } /** - * Throws a validation error for a giben column - * @param $col - * @throws \Exception\ValidationException + * throw validation exception for a model property + * @param string $col + * @param string $msg + * @throws ValidationException */ - protected function throwValidationError($col){ - throw new Exception\ValidationException('Validation failed: "' . $col . '".', $col); + protected function throwValidationException(string $col, string $msg = ''){ + $msg = empty($msg) ? 'Validation failed: "' . $col . '".' : $msg; + throw new ValidationException($msg, $col); + } + /** + * @param string $msg + * @throws DatabaseException + */ + protected function throwDbException(string $msg){ + throw new DatabaseException($msg); } /** @@ -458,11 +461,11 @@ abstract class BasicModel extends \DB\Cortex { * get single dataSet by id * @param $id * @param int $ttl + * @param bool $isActive * @return \DB\Cortex */ - public function getById($id, $ttl = 3) { - - return $this->getByForeignKey('id', (int)$id, ['limit' => 1], $ttl); + public function getById(int $id, int $ttl = 3, bool $isActive = true){ + return $this->getByForeignKey('id', (int)$id, ['limit' => 1], $ttl, $isActive); } /** @@ -492,10 +495,10 @@ abstract class BasicModel extends \DB\Cortex { * @param $value * @param array $options * @param int $ttl + * @param bool $isActive * @return \DB\Cortex */ - public function getByForeignKey($key, $value, $options = [], $ttl = 60){ - + public function getByForeignKey($key, $value, $options = [], $ttl = 0, $isActive = true){ $querySet = []; $query = []; if($this->exists($key)){ @@ -504,7 +507,7 @@ abstract class BasicModel extends \DB\Cortex { } // check active column - if($this->exists('active')){ + if($isActive && $this->exists('active')){ $query[] = "active = :active"; $querySet[':active'] = 1; } @@ -594,7 +597,7 @@ abstract class BasicModel extends \DB\Cortex { * function should be overwritten in parent classes * @return bool */ - public function isValid(){ + public function isValid(): bool { return true; } @@ -678,7 +681,7 @@ abstract class BasicModel extends \DB\Cortex { $status = $this->importStaticData($tableData); $this->getF3()->status(202); }else{ - $this->getF3()->error(502, 'File could not be read'); + $this->getF3()->error(500, 'File could not be read'); } }else{ $this->getF3()->error(404, 'File not found: ' . $filePath); @@ -727,15 +730,54 @@ abstract class BasicModel extends \DB\Cortex { } /** - * buffer a new activity (action) logging - * -> increment buffered counter - * -> log character activity create/update/delete events - * @param int $characterId - * @param int $mapId + * get "default" logging object for this kind of model + * -> can be overwritten * @param string $action + * @return Logging\LogInterface */ - protected function bufferActivity($characterId, $mapId, $action){ - Controller\LogController::instance()->bufferActivity($characterId, $mapId, $action); + protected function newLog($action = ''): Logging\LogInterface{ + return new Logging\DefaultLog($action); + } + + /** + * get formatter callback function for parsed logs + * @return null + */ + protected function getLogFormatter(){ + return null; + } + + /** + * add new validation error + * @param ValidationException $e + */ + protected function setValidationError(ValidationException $e){ + $this->validationError[] = $e->getError(); + } + + /** + * get all validation errors + * @return array + */ + public function getErrors(): array { + return $this->validationError; + } + + public function save(){ + try{ + return parent::save(); + }catch(ValidationException $e){ + $this->setValidationError($e); + }catch(DatabaseException $e){ + self::getF3()->error($e->getCode(), $e->getMessage(), $e->getTrace()); + } + } + + /** + * @return string + */ + public function __toString(){ + return $this->getTable(); } /** @@ -755,14 +797,14 @@ abstract class BasicModel extends \DB\Cortex { * @return BasicModel * @throws \Exception */ - public static function getNew($model, $ttl = 86400){ + public static function getNew($model, $ttl = self::DEFAULT_TTL){ $class = null; $model = '\\' . __NAMESPACE__ . '\\' . $model; if(class_exists($model)){ $class = new $model( null, null, null, $ttl ); }else{ - throw new \Exception('No model class found'); + throw new \Exception(sprintf(self::ERROR_INVALID_MODEL_CLASS, $model)); } return $class; diff --git a/app/main/model/characterlogmodel.php b/app/main/model/characterlogmodel.php index 1411a5bc..b978cd1e 100644 --- a/app/main/model/characterlogmodel.php +++ b/app/main/model/characterlogmodel.php @@ -57,6 +57,11 @@ class CharacterLogModel extends BasicModel { 'type' => Schema::DT_BIGINT, 'index' => true ], + 'shipMass' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], 'shipName' => [ 'type' => Schema::DT_VARCHAR128, 'nullable' => false, @@ -92,11 +97,13 @@ class CharacterLogModel extends BasicModel { $this->shipTypeName = $logData['ship']['typeName']; $this->shipId = (int)$logData['ship']['id']; $this->shipName = $logData['ship']['name']; + $this->shipMass = (float)$logData['ship']['mass']; }else{ $this->shipTypeId = null; $this->shipTypeName = ''; $this->shipId = null; $this->shipName = ''; + $this->shipMass = 0; } if( isset($logData['station']) ){ @@ -125,6 +132,7 @@ class CharacterLogModel extends BasicModel { $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; @@ -191,23 +199,26 @@ class CharacterLogModel extends BasicModel { * update session data for active character * @param int $systemId */ - protected function updateCharacterSessionLocation($systemId){ + protected function updateCharacterSessionLocation(int $systemId){ $controller = new Controller(); if( !empty($sessionCharacter = $controller->getSessionCharacterData()) && $sessionCharacter['ID'] === $this->get('characterId', true) ){ - $prevSystemId = (int)$sessionCharacter['PREV_SYSTEM_ID']; - - if($prevSystemId === 0){ + $systemChanged = false; + if((int)$sessionCharacter['PREV_SYSTEM_ID'] === 0){ $sessionCharacter['PREV_SYSTEM_ID'] = (int)$systemId; - }else{ + $systemChanged = true; + }elseif((int)$sessionCharacter['PREV_SYSTEM_ID'] !== $this->systemId){ $sessionCharacter['PREV_SYSTEM_ID'] = $this->systemId; + $systemChanged = true; } - $sessionCharacters = CharacterModel::mergeSessionCharacterData([$sessionCharacter]); - $this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacters); + if($systemChanged){ + $sessionCharacters = CharacterModel::mergeSessionCharacterData([$sessionCharacter]); + $this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacters); + } } } diff --git a/app/main/model/charactermodel.php b/app/main/model/charactermodel.php index 0259fc6e..62ee4da0 100644 --- a/app/main/model/charactermodel.php +++ b/app/main/model/charactermodel.php @@ -12,6 +12,8 @@ use Controller\Ccp\Sso as Sso; use Controller\Api\User as User; use DB\SQL\Schema; use Lib\Util; +use lib\Config; +use Model\Universe; class CharacterModel extends BasicModel { @@ -279,7 +281,7 @@ class CharacterModel extends BasicModel { if($minutes){ $seconds = $minutes * 60; - $timezone = new \DateTimeZone( self::getF3()->get('TZ') ); + $timezone = self::getF3()->get('getTimeZone')(); $kickedUntil = new \DateTime('now', $timezone); // add cookie expire time @@ -306,7 +308,7 @@ class CharacterModel extends BasicModel { $banned = null; if($status){ - $timezone = new \DateTimeZone( self::getF3()->get('TZ') ); + $timezone = self::getF3()->get('getTimeZone')(); $bannedSince = new \DateTime('now', $timezone); $banned = $bannedSince->format('Y-m-d H:i:s'); } @@ -479,7 +481,7 @@ class CharacterModel extends BasicModel { !empty($this->crestAccessToken) && !empty($this->crestAccessTokenUpdated) ){ - $timezone = new \DateTimeZone( self::getF3()->get('TZ') ); + $timezone = self::getF3()->get('getTimeZone')(); $tokenTime = \DateTime::createFromFormat( 'Y-m-d H:i:s', $this->crestAccessTokenUpdated, @@ -547,8 +549,8 @@ class CharacterModel extends BasicModel { if(is_null($this->banned)){ if( !$this->isKicked() ){ $f3 = self::getF3(); - $whitelistCorporations = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.CORPORATION') ) ); - $whitelistAlliance = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.ALLIANCE') ) ); + $whitelistCorporations = array_filter( array_map('trim', (array)Config::getPathfinderData('login.corporation') ) ); + $whitelistAlliance = array_filter( array_map('trim', (array)Config::getPathfinderData('login.alliance') ) ); if( empty($whitelistCorporations) && @@ -676,9 +678,7 @@ class CharacterModel extends BasicModel { if( !empty($locationData['system']['id']) ){ // character is currently in-game - // IDs for "systemId", "stationId and "shipTypeId" that require more data - $lookupIds = []; - + // get current $characterLog or get new --------------------------------------------------- if( !($characterLog = $this->getLog()) ){ // create new log $characterLog = $this->rel('characterLog'); @@ -687,12 +687,17 @@ class CharacterModel extends BasicModel { // get current log data and modify on change $logData = json_decode(json_encode( $characterLog->getData()), true); + // check system and station data for changes ---------------------------------------------- + + // IDs for "systemId", "stationId" that require more data + $lookupUniverseIds = []; + if( empty($logData['system']['name']) || $logData['system']['id'] !== $locationData['system']['id'] ){ // system changed -> request "system name" for current system - $lookupIds[] = $locationData['system']['id']; + $lookupUniverseIds[] = $locationData['system']['id']; } if( !empty($locationData['station']['id']) ){ @@ -701,7 +706,7 @@ class CharacterModel extends BasicModel { $logData['station']['id'] !== $locationData['station']['id'] ){ // station changed -> request "station name" for current station - $lookupIds[] = $locationData['station']['id']; + $lookupUniverseIds[] = $locationData['station']['id']; } }else{ unset($logData['station']); @@ -709,30 +714,10 @@ class CharacterModel extends BasicModel { $logData = array_replace_recursive($logData, $locationData); - // get current ship data - $shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions); - - if( !empty($shipData['ship']['typeId']) ){ - if( - empty($logData['ship']['typeName']) || - $logData['ship']['typeId'] !== $shipData['ship']['typeId'] - ){ - // ship changed -> request "station name" for current station - $lookupIds[] = $shipData['ship']['typeId']; - } - - // "shipName"/"shipId" could have changed... - $logData = array_replace_recursive($logData, $shipData); - }else{ - // ship data should never be empty -> keep current one - //unset($logData['ship']); - $invalidResponse = true; - } - - if( !empty($lookupIds) ){ + // get "more" data for systemId and/or stationId ----------------------------------------- + if( !empty($lookupUniverseIds) ){ // get "more" information for some Ids (e.g. name) - $universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupIds, $additionalOptions); - + $universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupUniverseIds, $additionalOptions); if( !empty($universeData) ){ $logData = array_replace_recursive($logData, $universeData); }else{ @@ -741,6 +726,48 @@ class CharacterModel extends BasicModel { } } + + // check ship data for changes ------------------------------------------------------------ + if( !$deleteLog ){ + $shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions); + + // IDs for "systemId", "stationId" that require more data + $lookupShipTypeId = 0; + + if( !empty($shipData['ship']['typeId']) ){ + if( + empty($logData['ship']['typeName']) || + $logData['ship']['typeId'] !== $shipData['ship']['typeId'] + ){ + // ship changed -> request "station name" for current station + $lookupShipTypeId = $shipData['ship']['typeId']; + } + + // "shipName"/"shipId" could have changed... + $logData = array_replace_recursive($logData, $shipData); + }else{ + // ship data should never be empty -> keep current one + //unset($logData['ship']); + $invalidResponse = true; + } + + // get "more" data for shipTypeId ---------------------------------------------------- + if($lookupShipTypeId > 0){ + /** + * @var $typeModel Universe\TypeModel + */ + $typeModel = Universe\BasicUniverseModel::getNew('TypeModel'); + $typeModel->loadById($lookupShipTypeId, $additionalOptions); + if(!$typeModel->dry()){ + $shipData['ship'] = (array)$typeModel->getShipData(); + $logData = array_replace_recursive($logData, $shipData); + }else{ + // this is important! ship data is a MUST HAVE! + $deleteLog = true; + } + } + } + if( !$deleteLog ){ // mark log as "updated" even if no changes were made if($additionalOptions['markUpdated'] === true){ @@ -921,7 +948,7 @@ class CharacterModel extends BasicModel { $mapCountPrivate = 0; foreach($this->characterMaps as $characterMap){ if( - $mapCountPrivate < self::getF3()->get('PATHFINDER.MAP.PRIVATE.MAX_COUNT') && + $mapCountPrivate < Config::getMapsDefaultConfig('private')['max_count'] && $characterMap->mapId->isActive() ){ $maps[] = $characterMap->mapId; @@ -934,11 +961,19 @@ class CharacterModel extends BasicModel { } /** - * character logout - * -> clear authentication data + * delete current location */ - public function logout(){ - if( is_object($this->characterAuthentications) ){ + protected function deleteLog(){ + if($characterLog = $this->getLog()){ + $characterLog->erase(); + } + } + + /** + * delete authentications data + */ + protected function deleteAuthentications(){ + if(is_object($this->characterAuthentications)){ foreach($this->characterAuthentications as $characterAuthentication){ /** * @var $characterAuthentication CharacterAuthenticationModel @@ -947,6 +982,40 @@ class CharacterModel extends BasicModel { } } } + /** + * character logout + * @param bool $deleteLog + * @param bool $deleteSession + * @param bool $deleteCookie + */ + public function logout(bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){ + // delete current session data -------------------------------------------------------------------------------- + if($deleteSession){ + $sessionCharacterData = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS); + $sessionCharacterData = array_filter($sessionCharacterData, function($data){ + return ($data['ID'] != $this->_id); + }); + + if(empty($sessionCharacterData)){ + // no active characters logged in -> log user out + $this->getF3()->clear(User::SESSION_KEY_USER); + $this->getF3()->clear(User::SESSION_KEY_CHARACTERS); + }else{ + // update remaining active characters + $this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacterData); + } + } + + // delete current location data ------------------------------------------------------------------------------- + if($deleteLog){ + $this->deleteLog(); + } + + // delete auth cookie data ------------------------------------------------------------------------------------ + if($deleteCookie ){ + $this->deleteAuthentications(); + } + } /** * merges two multidimensional characterSession arrays by checking characterID diff --git a/app/main/model/connectionmodel.php b/app/main/model/connectionmodel.php index 29f77e25..bdc2ed8d 100644 --- a/app/main/model/connectionmodel.php +++ b/app/main/model/connectionmodel.php @@ -9,10 +9,10 @@ namespace Model; use DB\SQL\Schema; -use Controller; use Controller\Api\Route; +use Lib\Logging; -class ConnectionModel extends BasicModel{ +class ConnectionModel extends AbstractMapTrackingModel { protected $table = 'connection'; @@ -79,10 +79,16 @@ class ConnectionModel extends BasicModel{ /** * set an array with all data for a system - * @param $systemData + * @param array $data */ - public function setData($systemData){ - foreach((array)$systemData as $key => $value){ + public function setData($data){ + unset($data['id']); + unset($data['created']); + unset($data['updated']); + unset($data['createdCharacterId']); + unset($data['updatedCharacterId']); + + foreach((array)$data as $key => $value){ if( !is_array($value) ){ if( $this->exists($key) ){ $this->$key = $value; @@ -100,7 +106,6 @@ class ConnectionModel extends BasicModel{ * @return \stdClass */ public function getData($addSignatureData = false){ - $connectionData = (object) []; $connectionData->id = $this->id; $connectionData->source = $this->source->id; @@ -189,21 +194,21 @@ class ConnectionModel extends BasicModel{ * check whether this model is valid or not * @return bool */ - public function isValid(){ - $isValid = true; - - // check if source/target system are not equal - // check if source/target belong to same map - if( - is_object($this->source) && - is_object($this->target) && - $this->get('source', true) === $this->get('target', true) || - $this->source->get('mapId', true) !== $this->target->get('mapId', true) - ){ - $isValid = false; + public function isValid(): bool { + if($valid = parent::isValid()){ + // check if source/target system are not equal + // check if source/target belong to same map + if( + is_object($this->source) && + is_object($this->target) && + $this->get('source', true) === $this->get('target', true) || + $this->source->get('mapId', true) !== $this->target->get('mapId', true) + ){ + $valid = false; + } } - return $isValid; + return $valid; } /** @@ -218,7 +223,6 @@ class ConnectionModel extends BasicModel{ // check for "default" connection type and add them if missing // -> get() with "true" returns RAW data! important for JSON table column check! $types = (array)json_decode( $this->get('type', true) ); - if( !$this->scope || empty($types) @@ -226,7 +230,7 @@ class ConnectionModel extends BasicModel{ $this->setDefaultTypeData(); } - return parent::beforeInsertEvent($self, $pkeys); + return $this->isValid() ? parent::beforeInsertEvent($self, $pkeys) : false; } /** @@ -263,35 +267,18 @@ class ConnectionModel extends BasicModel{ } /** - * log character activity create/update/delete events * @param string $action + * @return Logging\LogInterface */ - protected function logActivity($action){ - - if( - $this->enableActivityLogging && - ( - $action === 'connectionDelete' || - !empty($this->fieldChanges) - ) && - $this->get('mapId')->isActivityLogEnabled() - ){ - // TODO implement "dependency injection" for active character object... - $controller = new Controller\Controller(); - $currentActiveCharacter = $controller->getCharacter(); - $characterId = is_null($currentActiveCharacter) ? 0 : $currentActiveCharacter->_id; - $mapId = $this->get('mapId', true); - - parent::bufferActivity($characterId, $mapId, $action); - } + public function newLog($action = ''): Logging\LogInterface{ + return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); } /** - * save connection and check if obj is valid - * @return ConnectionModel|false + * @return MapModel */ - public function save(){ - return ( $this->isValid() ) ? parent::save() : false; + public function getMap(): MapModel{ + return $this->get('mapId'); } /** @@ -307,6 +294,17 @@ class ConnectionModel extends BasicModel{ } } + /** + * get object relevant data for model log + * @return array + */ + public function getLogObjectData() : array{ + return [ + 'objId' => $this->_id, + 'objName' => $this->scope + ]; + } + /** * see parent */ diff --git a/app/main/model/corporationmodel.php b/app/main/model/corporationmodel.php index 02cfc789..a503d7df 100644 --- a/app/main/model/corporationmodel.php +++ b/app/main/model/corporationmodel.php @@ -9,6 +9,7 @@ namespace Model; use DB\SQL\Schema; +use lib\Config; class CorporationModel extends BasicModel { @@ -131,8 +132,6 @@ class CorporationModel extends BasicModel { public function getMaps(){ $maps = []; - $f3 = self::getF3(); - $this->filter('mapCorporations', ['active = ?', 1], ['order' => 'created'] @@ -143,7 +142,7 @@ class CorporationModel extends BasicModel { foreach($this->mapCorporations as $mapCorporation){ if( $mapCorporation->mapId->isActive() && - $mapCount < $f3->get('PATHFINDER.MAP.CORPORATION.MAX_COUNT') + $mapCount < Config::getMapsDefaultConfig('corporation')['max_count'] ){ $maps[] = $mapCorporation->mapId; $mapCount++; diff --git a/app/main/model/logmodelinterface.php b/app/main/model/logmodelinterface.php new file mode 100644 index 00000000..cca816aa --- /dev/null +++ b/app/main/model/logmodelinterface.php @@ -0,0 +1,19 @@ + [ @@ -31,7 +32,7 @@ class MapModel extends BasicModel { 'nullable' => false, 'default' => 1, 'index' => true, - 'after' => 'updated' + 'activity-log' => true ], 'scopeId' => [ 'type' => Schema::DT_INT, @@ -42,7 +43,9 @@ class MapModel extends BasicModel { 'table' => 'map_scope', 'on-delete' => 'CASCADE' ] - ] + ], + 'validate' => 'validate_notDry', + 'activity-log' => true ], 'typeId' => [ 'type' => Schema::DT_INT, @@ -53,32 +56,82 @@ class MapModel extends BasicModel { 'table' => 'map_type', 'on-delete' => 'CASCADE' ] - ] + ], + 'validate' => 'validate_notDry', + 'activity-log' => true ], 'name' => [ 'type' => Schema::DT_VARCHAR128, 'nullable' => false, - 'default' => '' + 'default' => '', + 'activity-log' => true, + 'validate' => true ], 'icon' => [ 'type' => Schema::DT_VARCHAR128, 'nullable' => false, - 'default' => '' + 'default' => '', + 'activity-log' => true ], 'deleteExpiredConnections' => [ 'type' => Schema::DT_BOOL, 'nullable' => false, - 'default' => 1 + 'default' => 1, + 'activity-log' => true ], 'deleteEolConnections' => [ 'type' => Schema::DT_BOOL, 'nullable' => false, - 'default' => 1 + 'default' => 1, + 'activity-log' => true ], 'persistentAliases' => [ 'type' => Schema::DT_BOOL, 'nullable' => false, - 'default' => 1 + 'default' => 1, + 'activity-log' => true + ], + 'logActivity' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 1, + 'activity-log' => true + ], + 'logHistory' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 0, + 'activity-log' => true + ], + 'slackWebHookURL' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '', + 'validate' => true + ], + 'slackUsername' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '', + 'activity-log' => true + ], + 'slackIcon' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '', + 'activity-log' => true + ], + 'slackChannelHistory' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '', + 'activity-log' => true + ], + 'slackChannelRally' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '', + 'activity-log' => true ], 'systems' => [ 'has-many' => ['Model\SystemModel', 'mapId'] @@ -97,37 +150,18 @@ class MapModel extends BasicModel { ] ]; - protected $validate = [ - 'name' => [ - 'length' => [ - 'min' => 3 - ] - ], - 'icon' => [ - 'length' => [ - 'min' => 3 - ] - ], - 'scopeId' => [ - 'regex' => '/^[1-9]+$/' - ], - 'typeId' => [ - 'regex' => '/^[1-9]+$/' - ] - ]; - /** * set map data by an associative array - * @param $data + * @param array $data */ public function setData($data){ + unset($data['id']); + unset($data['created']); + unset($data['updated']); + unset($data['createdCharacterId']); + unset($data['updatedCharacterId']); foreach((array)$data as $key => $value){ - - if($key == 'created'){ - continue; - } - if(!is_array($value)){ if($this->exists($key)){ $this->$key = $value; @@ -155,35 +189,62 @@ class MapModel extends BasicModel { if(is_null($mapDataAll)){ // no cached map data found - $mapData = (object) []; - $mapData->id = $this->id; - $mapData->name = $this->name; - $mapData->icon = $this->icon; - $mapData->deleteExpiredConnections = $this->deleteExpiredConnections; - $mapData->deleteEolConnections = $this->deleteEolConnections; - $mapData->persistentAliases = $this->persistentAliases; - $mapData->created = strtotime($this->created); - $mapData->updated = strtotime($this->updated); + $mapData = (object) []; + $mapData->id = $this->id; + $mapData->name = $this->name; + $mapData->icon = $this->icon; + $mapData->deleteExpiredConnections = $this->deleteExpiredConnections; + $mapData->deleteEolConnections = $this->deleteEolConnections; + $mapData->persistentAliases = $this->persistentAliases; // map scope - $mapData->scope = (object) []; - $mapData->scope->id = $this->scopeId->id; - $mapData->scope->name = $this->scopeId->name; - $mapData->scope->label = $this->scopeId->label; + $mapData->scope = (object) []; + $mapData->scope->id = $this->scopeId->id; + $mapData->scope->name = $this->scopeId->name; + $mapData->scope->label = $this->scopeId->label; // map type - $mapData->type = (object) []; - $mapData->type->id = $this->typeId->id; - $mapData->type->name = $this->typeId->name; - $mapData->type->classTab = $this->typeId->classTab; + $mapData->type = (object) []; + $mapData->type->id = $this->typeId->id; + $mapData->type->name = $this->typeId->name; + $mapData->type->classTab = $this->typeId->classTab; + + // map logging + $mapData->logging = (object) []; + $mapData->logging->activity = $this->isActivityLogEnabled(); + $mapData->logging->history = $this->isHistoryLogEnabled(); + + // map Slack logging + $mapData->logging->slackHistory = $this->isSlackChannelEnabled('slackChannelHistory'); + $mapData->logging->slackRally = $this->isSlackChannelEnabled('slackChannelRally'); + $mapData->logging->slackWebHookURL = $this->slackWebHookURL; + $mapData->logging->slackUsername = $this->slackUsername; + $mapData->logging->slackIcon = $this->slackIcon; + $mapData->logging->slackChannelHistory = $this->slackChannelHistory; + $mapData->logging->slackChannelRally = $this->slackChannelRally; + + // map mail logging + $mapData->logging->mailRally = $this->isMailSendEnabled('RALLY_SET'); // map access - $mapData->access = (object) []; - $mapData->access->character = []; - $mapData->access->corporation = []; - $mapData->access->alliance = []; + $mapData->access = (object) []; + $mapData->access->character = []; + $mapData->access->corporation = []; + $mapData->access->alliance = []; - // get access object data ------------------------------------- + $mapData->created = (object) []; + $mapData->created->created = strtotime($this->created); + if(is_object($this->createdCharacterId)){ + $mapData->created->character = $this->createdCharacterId->getData(); + } + + $mapData->updated = (object) []; + $mapData->updated->updated = strtotime($this->updated); + if(is_object($this->updatedCharacterId)){ + $mapData->updated->character = $this->updatedCharacterId->getData(); + } + + // get access object data --------------------------------------------------------------------------------- if($this->isPrivate()){ $characters = $this->getCharacters(); $characterData = []; @@ -209,14 +270,14 @@ class MapModel extends BasicModel { $mapData->access->alliance = $allianceData; } - // merge all data --------------------------------------------- + // merge all data ----------------------------------------------------------------------------------------- $mapDataAll = (object) []; $mapDataAll->mapData = $mapData; - // map system data -------------------------------------------- + // map system data ---------------------------------------------------------------------------------------- $mapDataAll->systems = $this->getSystemData(); - // map connection data ---------------------------------------- + // map connection data ------------------------------------------------------------------------------------ $mapDataAll->connections = $this->getConnectionData(); // max caching time for a map @@ -228,6 +289,70 @@ class MapModel extends BasicModel { return $mapDataAll; } + /** + * validate name column + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_name(string $key, string $val): bool { + $valid = true; + if(mb_strlen($val) < 3){ + $valid = false; + $this->throwValidationException($key); + } + return $valid; + } + + /** + * validate Slack WebHook URL + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_slackWebHookURL(string $key, string $val): bool { + $valid = true; + if( !empty($val) ){ + if( + !\Audit::instance()->url($val) || + parse_url($val, PHP_URL_HOST) !== 'hooks.slack.com' + ){ + $valid = false; + $this->throwValidationException($key); + } + } + return $valid; + } + + /** + * @param $channel + * @return string + */ + protected function set_slackChannelHistory($channel){ + return $this->formatSlackChannelName($channel); + } + + /** + * @param $channel + * @return string + */ + protected function set_slackChannelRally($channel){ + return $this->formatSlackChannelName($channel); + } + + /** + * convert a Slack channel name into correct format + * @param $channel + * @return string + */ + private function formatSlackChannelName($channel){ + $channel = strtolower(str_replace(' ','', trim(trim((string)$channel), '#@'))); + if($channel){ + $channel = '#' . $channel; + } + return $channel; + } + /** * Event "Hook" function * @param self $self @@ -235,6 +360,7 @@ class MapModel extends BasicModel { */ public function afterInsertEvent($self, $pkeys){ $self->clearCacheData(); + $self->logActivity('mapCreate'); } /** @@ -244,6 +370,9 @@ class MapModel extends BasicModel { */ public function afterUpdateEvent($self, $pkeys){ $self->clearCacheData(); + + $activity = ($self->isActive()) ? 'mapUpdate' : 'mapDelete'; + $self->logActivity($activity); } /** @@ -253,6 +382,8 @@ class MapModel extends BasicModel { */ public function afterEraseEvent($self, $pkeys){ $self->clearCacheData(); + $self->logActivity('mapDelete'); + $self->deleteLogFile(); } /** @@ -701,49 +832,185 @@ class MapModel extends BasicModel { } /** - * delete this map and all dependencies - * @param CharacterModel $characterModel - * @param null $callback + * @param string $action + * @return Logging\LogInterface */ - public function delete(CharacterModel $characterModel, $callback = null){ + public function newLog($action = ''): Logging\LogInterface{ + $logChannelData = $this->getLogChannelData(); + $logObjectData = $this->getLogObjectData(); + $log = (new Logging\MapLog($action, $logChannelData))->setTempData($logObjectData); - if( !$this->dry() ){ - // check if character has access - if($this->hasAccess($characterModel)){ - // all map related tables will be deleted on cascade - if( - $this->erase() && - is_callable($callback) - ){ - $callback($this->_id); - } + // update map history *.log files ----------------------------------------------------------------------------- + if($this->isHistoryLogEnabled()){ + // check socket config + if(Config::validSocketConnect()){ + $log->addHandler('zmq', 'json', $this->getSocketConfig()); + }else{ + // update log file local (slow) + $log->addHandler('stream', 'json', $this->getStreamConfig()); } } + + // send map history to Slack channel -------------------------------------------------------------------------- + $slackChannelKey = 'slackChannelHistory'; + if($this->isSlackChannelEnabled($slackChannelKey)){ + $log->addHandler('slackMap', null, $this->getSlackWebHookConfig($slackChannelKey)); + $log->addHandlerGroup('slackMap'); + } + + // update map activity ---------------------------------------------------------------------------------------- + $log->logActivity($this->isActivityLogEnabled()); + + return $log; + } + + /** + * @return MapModel + */ + public function getMap(): MapModel{ + return $this; + } + + /** + * get object relevant data for model log channel + * @return array + */ + public function getLogChannelData() : array{ + return [ + 'channelId' => $this->_id, + 'channelName' => $this->name + ]; + } + /** + * get object relevant data for model log object + * @return array + */ + public function getLogObjectData() : array{ + return [ + 'objId' => $this->_id, + 'objName' => $this->name + ]; + } + + protected function getLogFormatter(){ + return function(&$rowDataObj){ + unset($rowDataObj['extra']); + }; } /** * check if "activity logging" is enabled for this map type * @return bool */ - public function isActivityLogEnabled(){ - $f3 = self::getF3(); - $activityLogEnabled = false; + public function isActivityLogEnabled(): bool { + return $this->logActivity && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_activity_enabled']; + } - if( $this->isAlliance() ){ - if( $f3->get('PATHFINDER.MAP.ALLIANCE.ACTIVITY_LOGGING') ){ - $activityLogEnabled = true; + /** + * check if "history logging" is enabled for this map type + * @return bool + */ + public function isHistoryLogEnabled(): bool { + return $this->logHistory && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_history_enabled']; + } + + /** + * check if "Slack WebHook" is enabled for this map type + * @param string $channel + * @return bool + * @throws PathfinderException + */ + public function isSlackChannelEnabled(string $channel): bool { + $enabled = false; + // check global Slack status + if((bool)Config::getPathfinderData('slack.status')){ + // check global map default config for this channel + switch($channel){ + case 'slackChannelHistory': $defaultMapConfigKey = 'send_history_slack_enabled'; break; + case 'slackChannelRally': $defaultMapConfigKey = 'send_rally_slack_enabled'; break; + default: throw new PathfinderException(sprintf(self::ERROR_SLACK_CHANNEL, $channel)); } - }elseif( $this->isCorporation() ){ - if( $f3->get('PATHFINDER.MAP.CORPORATION.ACTIVITY_LOGGING') ){ - $activityLogEnabled = true; - } - }elseif( $this->isPrivate() ){ - if( $f3->get('PATHFINDER.MAP.PRIVATE.ACTIVITY_LOGGING') ){ - $activityLogEnabled = true; + + if((bool) Config::getMapsDefaultConfig($this->typeId->name)[$defaultMapConfigKey]){ + $config = $this->getSlackWebHookConfig($channel); + if($config->slackWebHookURL && $config->slackChannel){ + $enabled = true; + } } } - return $activityLogEnabled; + return $enabled; + } + + /** + * check if "E-Mail" Log is enabled for this map + * @param string $type + * @return bool + */ + public function isMailSendEnabled(string $type): bool{ + $enabled = false; + if((bool) Config::getMapsDefaultConfig($this->typeId->name)['send_rally_mail_enabled']){ + $enabled = Config::isValidSMTPConfig($this->getSMTPConfig($type)); + } + + return $enabled; + } + + /** + * get config for stream logging + * @param bool $abs absolute path + * @return \stdClass + */ + public function getStreamConfig(bool $abs = false): \stdClass{ + $config = (object) []; + $config->stream = ''; + if( $this->getF3()->exists('PATHFINDER.HISTORY.LOG', $dir) ){ + $config->stream .= $abs ? $this->getF3()->get('ROOT') . '/' : './'; + $config->stream .= $dir . 'map/map_' . $this->_id . '.log'; + $config->stream = $this->getF3()->fixslashes($config->stream); + } + return $config; + } + + /** + * get config for Socket connection (e.g. where to send log data) + * @return \stdClass + */ + public function getSocketConfig(): \stdClass{ + $config = (object) []; + $config->uri = Config::getSocketUri(); + $config->streamConf = $this->getStreamConfig(true); + return $config; + } + + /** + * get Config for Slack WebHook cURL calls + * -> https://api.slack.com/incoming-webhooks + * @param string $channel + * @return \stdClass + */ + public function getSlackWebHookConfig(string $channel = ''): \stdClass{ + $config = (object) []; + $config->slackWebHookURL = $this->slackWebHookURL; + $config->slackUsername = $this->slackUsername; + $config->slackIcon = $this->slackIcon; + if($channel && $this->exists($channel)){ + $config->slackChannel = $this->$channel; + } + return $config; + } + + /** + * get Config for SMTP connection and recipient address + * @param string $type + * @param bool $addJson + * @return \stdClass + */ + public function getSMTPConfig(string $type, bool $addJson = true): \stdClass{ + $config = Config::getSMTPConfig(); + $config->to = Config::getNotificationMail($type); + $config->addJson = $addJson; + return $config; } /** @@ -782,22 +1049,31 @@ class MapModel extends BasicModel { return $scope; } + /** + * get log file data + * @param int $offset + * @param int $limit + * @return array + */ + public function getLogData(int $offset = FileHandler::LOG_FILE_OFFSET, int $limit = FileHandler::LOG_FILE_LIMIT): array { + $streamConf = $this->getStreamConfig(); + return FileHandler::readLogFile($streamConf->stream, $offset, $limit, $this->getLogFormatter()); + } + /** * save a system to this map * @param SystemModel $system + * @param CharacterModel $character * @param int $posX * @param int $posY - * @param null|CharacterModel $character - * @return mixed + * @return false|ConnectionModel */ - public function saveSystem( SystemModel $system, $posX = 10, $posY = 0, $character = null){ + public function saveSystem( SystemModel $system, CharacterModel $character, $posX = 10, $posY = 0){ $system->setActive(true); $system->mapId = $this->id; $system->posX = $posX; $system->posY = $posY; - $system->createdCharacterId = $character; - $system->updatedCharacterId = $character; - return $system->save(); + return $system->save($character); } /** @@ -839,11 +1115,26 @@ class MapModel extends BasicModel { * save new connection * -> connection scope/type is automatically added * @param ConnectionModel $connection + * @param CharacterModel $character * @return false|ConnectionModel */ - public function saveConnection(ConnectionModel $connection){ + public function saveConnection(ConnectionModel $connection, CharacterModel $character){ $connection->mapId = $this; - return $connection->save(); + return $connection->save($character); + } + + /** + * delete existing log file + */ + protected function deleteLogFile(){ + $config = $this->getStreamConfig(); + if(is_file($config->stream)){ + // try to set write access + if(!is_writable($config->stream)){ + chmod($config->stream, 0666); + } + @unlink($config->stream); + } } /** @@ -909,12 +1200,11 @@ class MapModel extends BasicModel { } /** - * save a map - * @return mixed + * @param CharacterModel|null $characterModel + * @return false|ConnectionModel */ - public function save(){ - - $mapModel = parent::save(); + public function save(CharacterModel $characterModel = null){ + $mapModel = parent::save($characterModel); // check if map type has changed and clear access objects if( !$mapModel->dry() ){ diff --git a/app/main/model/systemmodel.php b/app/main/model/systemmodel.php index a8b786f1..9d8e5984 100644 --- a/app/main/model/systemmodel.php +++ b/app/main/model/systemmodel.php @@ -8,11 +8,10 @@ namespace Model; -use controller\MailController; use DB\SQL\Schema; -use lib\Config; +use Lib\Logging; -class SystemModel extends BasicModel { +class SystemModel extends AbstractMapTrackingModel { const MAX_POS_X = 2300; const MAX_POS_Y = 498; @@ -124,7 +123,8 @@ class SystemModel extends BasicModel { 'rallyPoke' => [ 'type' => Schema::DT_BOOL, 'nullable' => false, - 'default' => 0 + 'default' => 0, + 'activity-log' => true ], 'description' => [ 'type' => Schema::DT_VARCHAR512, @@ -142,28 +142,6 @@ class SystemModel extends BasicModel { 'nullable' => false, 'default' => 0 ], - 'createdCharacterId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\CharacterModel', - 'constraint' => [ - [ - 'table' => 'character', - 'on-delete' => 'CASCADE' - ] - ] - ], - 'updatedCharacterId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\CharacterModel', - 'constraint' => [ - [ - 'table' => 'character', - 'on-delete' => 'CASCADE' - ] - ] - ], 'signatures' => [ 'has-many' => ['Model\SystemSignatureModel', 'systemId'] ], @@ -177,16 +155,16 @@ class SystemModel extends BasicModel { /** * set an array with all data for a system - * @param array $systemData + * @param array $data */ - public function setData($systemData){ - - foreach((array)$systemData as $key => $value){ - - if($key == 'created'){ - continue; - } + public function setData($data){ + unset($data['id']); + unset($data['created']); + unset($data['updated']); + unset($data['createdCharacterId']); + unset($data['updatedCharacterId']); + foreach((array)$data as $key => $value){ if(!is_array($value)){ if($this->exists($key)){ $this->$key = $value; @@ -361,8 +339,6 @@ class SystemModel extends BasicModel { case 1: // new rally point set $rally = date('Y-m-d H:i:s', time()); - // flag system for mail poke -> after save() - $this->virtual('newRallyPointSet', true); break; default: $rally = date('Y-m-d H:i:s', $rally); @@ -417,15 +393,6 @@ class SystemModel extends BasicModel { */ public function afterUpdateEvent($self, $pkeys){ $self->clearCacheData(); - - // check if rally point mail should be send - if( - $self->newRallyPointSet && - $self->rallyPoke - ){ - $self->sendRallyPointMail(); - } - $activity = ($self->isActive()) ? 'systemUpdate' : 'systemDelete'; $self->logActivity($activity); } @@ -441,23 +408,18 @@ class SystemModel extends BasicModel { } /** - * log character activity create/update/delete events * @param string $action + * @return Logging\LogInterface */ - protected function logActivity($action){ - if( - $this->enableActivityLogging && - ( - $action === 'systemDelete' || - !empty($this->fieldChanges) - ) && - $this->get('mapId')->isActivityLogEnabled() - ){ - $characterId = $this->get('updatedCharacterId', true); - $mapId = $this->get('mapId', true); + public function newLog($action = ''): Logging\LogInterface{ + return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); + } - parent::bufferActivity($characterId, $mapId, $action); - } + /** + * @return MapModel + */ + public function getMap(): MapModel{ + return $this->get('mapId'); } /** @@ -599,6 +561,52 @@ class SystemModel extends BasicModel { return ($this->isWormhole() && $this->security === 'SH'); } + /** + * send rally point poke to various "APIs" + * -> send to a Slack channel + * -> send to an Email + * @param array $rallyData + * @param CharacterModel $characterModel + */ + public function sendRallyPoke(array $rallyData, CharacterModel $characterModel){ + // rally log needs at least one handler to be valid + $isValidLog = false; + $log = new Logging\RallyLog('rallySet', $this->getMap()->getLogChannelData()); + + // Slack poke ----------------------------------------------------------------------------- + $slackChannelKey = 'slackChannelRally'; + if( + $rallyData['pokeSlack'] === true && + $this->getMap()->isSlackChannelEnabled($slackChannelKey) + ){ + $isValidLog = true; + $log->addHandler('slackRally', null, $this->getMap()->getSlackWebHookConfig($slackChannelKey)); + } + + // Mail poke ------------------------------------------------------------------------------ + $mailAddressKey = 'RALLY_SET'; + if( + $rallyData['pokeMail'] === true && + $this->getMap()->isMailSendEnabled('RALLY_SET') + ){ + $isValidLog = true; + $mailConf = $this->getMap()->getSMTPConfig($mailAddressKey, false); + $log->addHandler('mail', 'mail', $mailConf); + } + + // Buffer log ----------------------------------------------------------------------------- + if($isValidLog){ + $log->setTempData($this->getLogObjectData(true)); + $log->setCharacter($characterModel); + if( !empty($rallyData['message']) ){ + $log->setData([ + 'message' => $rallyData['message'] + ]); + } + $log->buffer(); + } + } + /** * get static WH data for this system * -> any WH system has at least one static WH @@ -641,34 +649,27 @@ class SystemModel extends BasicModel { } /** - * send rally point information by mail + * get object relevant data for model log + * @param bool $fullData + * @return array */ - protected function sendRallyPointMail(){ - $recipient = Config::getNotificationMail('RALLY_SET'); + public function getLogObjectData($fullData = false) : array{ + $objectData = [ + 'objId' => $this->_id, + 'objName' => $this->name + ]; - if( - $recipient && - \Audit::instance()->email($recipient) - ){ - $updatedCharacterId = (int) $this->get('updatedCharacterId', true); - /** - * @var $character CharacterModel - */ - $character = $this->rel('updatedCharacterId'); - $character->getById( $updatedCharacterId ); - if( !$character->dry() ){ - $body = []; - $body[] = "Map:\t\t" . $this->mapId->name; - $body[] = "System:\t\t" . $this->name; - $body[] = "Region:\t\t" . $this->region; - $body[] = "Security:\t" . $this->security; - $body[] = "Character:\t" . $character->name; - $body[] = "Time:\t\t" . date('g:i a; F j, Y', strtotime($this->rallyUpdated) ); - $bodyMsg = implode("\r\n", $body); - - (new MailController())->sendRallyPoint($recipient, $bodyMsg); - } + if($fullData){ + $objectData['objAlias'] = $this->alias; + $objectData['objRegion'] = $this->region; + $objectData['objIsWormhole'] = $this->isWormhole(); + $objectData['objEffect'] = $this->effect; + $objectData['objSecurity'] = $this->security; + $objectData['objTrueSec'] = $this->trueSec; + $objectData['objDescription'] = $this->description; } + + return $objectData; } /** diff --git a/app/main/model/systemsignaturemodel.php b/app/main/model/systemsignaturemodel.php index 553301af..402e8c9c 100644 --- a/app/main/model/systemsignaturemodel.php +++ b/app/main/model/systemsignaturemodel.php @@ -9,8 +9,9 @@ namespace Model; use DB\SQL\Schema; +use Lib\Logging; -class SystemSignatureModel extends BasicModel { +class SystemSignatureModel extends AbstractMapTrackingModel { protected $table = 'system_signature'; @@ -62,52 +63,29 @@ class SystemSignatureModel extends BasicModel { 'type' => Schema::DT_VARCHAR128, 'nullable' => false, 'default' => '', - 'activity-log' => true + 'activity-log' => true, + 'validate' => true ], 'description' => [ 'type' => Schema::DT_VARCHAR512, 'nullable' => false, 'default' => '', 'activity-log' => true - ], - 'createdCharacterId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\CharacterModel', - 'constraint' => [ - [ - 'table' => 'character', - 'on-delete' => 'CASCADE' - ] - ] - ], - 'updatedCharacterId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\CharacterModel', - 'constraint' => [ - [ - 'table' => 'character', - 'on-delete' => 'CASCADE' - ] - ] - ] - ]; - - protected $validate = [ - 'name' => [ - 'length' => [ - 'min' => 3 - ] ] ]; /** * set an array with all data for a system - * @param $signatureData + * @param $data */ - public function setData($signatureData){ - foreach((array)$signatureData as $key => $value){ + public function setData($data){ + unset($data['id']); + unset($data['created']); + unset($data['updated']); + unset($data['createdCharacterId']); + unset($data['updatedCharacterId']); + + foreach((array)$data as $key => $value){ if(!is_array($value)){ if($this->exists($key)){ $this->$key = $value; @@ -121,7 +99,6 @@ class SystemSignatureModel extends BasicModel { * @return \stdClass */ public function getData(){ - $signatureData = (object) []; $signatureData->id = $this->id; @@ -187,6 +164,36 @@ class SystemSignatureModel extends BasicModel { return $validConnectionId; } + /** + * validate name column + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_name(string $key, string $val): bool { + $valid = true; + if(mb_strlen($val) < 3){ + $valid = false; + $this->throwValidationException($key); + } + return $valid; + } + + /** + * @param string $action + * @return Logging\LogInterface + */ + public function newLog($action = ''): Logging\LogInterface{ + return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); + } + + /** + * @return MapModel + */ + public function getMap(): MapModel{ + return $this->get('systemId')->getMap(); + } + /** * get the connection (if attached) * @return \Model\ConnectionModel|null @@ -270,29 +277,14 @@ class SystemSignatureModel extends BasicModel { } /** - * log character activity create/update/delete events - * @param string $action + * get object relevant data for model log + * @return array */ - protected function logActivity($action){ - if($this->enableActivityLogging){ - /** - * @var $map MapModel - */ - $map = $this->get('systemId')->get('mapId'); - - if( - ( - $action === 'signatureDelete' || - !empty($this->fieldChanges) - ) && - $map->isActivityLogEnabled() - ){ - $characterId = $this->get('updatedCharacterId', true); - $mapId = $map->_id; - - parent::bufferActivity($characterId, $mapId, $action); - } - } + public function getLogObjectData() : array{ + return [ + 'objId' => $this->_id, + 'objName' => $this->name + ]; } /** diff --git a/app/main/model/universe/basicuniversemodel.php b/app/main/model/universe/basicuniversemodel.php index eb2ac4d1..681ab116 100644 --- a/app/main/model/universe/basicuniversemodel.php +++ b/app/main/model/universe/basicuniversemodel.php @@ -9,9 +9,30 @@ namespace Model\Universe; +use DB\Database; use Model\BasicModel; -class BasicUniverseModel extends BasicModel { +abstract class BasicUniverseModel extends BasicModel { + + /** + * data from Universe tables is static and does not change frequently + * -> refresh static data after X days + */ + const CACHE_MAX_DAYS = 7; protected $db = 'DB_UNIVERSE'; + + public static function getNew($model, $ttl = self::DEFAULT_TTL){ + $class = null; + + $model = '\\' . __NAMESPACE__ . '\\' . $model; + if(class_exists($model)){ + $db = Database::instance()->getDB('UNIVERSE'); + $class = new $model($db, null, null, $ttl); + }else{ + throw new \Exception(sprintf(self::ERROR_INVALID_MODEL_CLASS, $model)); + } + + return $class; + } } \ No newline at end of file diff --git a/app/main/model/universe/typemodel.php b/app/main/model/universe/typemodel.php new file mode 100644 index 00000000..7db8476a --- /dev/null +++ b/app/main/model/universe/typemodel.php @@ -0,0 +1,145 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'description' => [ + 'type' => Schema::DT_TEXT + ], + 'published' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 1, + 'index' => true + ], + 'radius' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'volume' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'capacity' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'mass' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'groupId' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ], + 'marketGroupId' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ], + 'packagedVolume' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'portionSize' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ], + 'graphicId' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ] + ]; + + /** + * get shipData from object + * -> more fields can be added in here if needed + * @return \stdClass + */ + public function getShipData(): \stdClass { + $shipData = (object) []; + if(!$this->dry()){ + $shipData->typeId = $this->_id; + $shipData->typeName = $this->name; + $shipData->mass = $this->mass; + } + return $shipData; + } + + /** + * load data from API into $this and save $this + * @param int $id + * @param array $additionalOptions + */ + protected function loadData(int $id, array $additionalOptions = []){ + $data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions); + if(!empty($data)){ + $this->copyfrom($data); + $this->save(); + } + } + + /** + * load object by $id + * -> if $id not exists in DB -> query API + * @param int $id + * @param array $additionalOptions + */ + public function loadById(int $id, array $additionalOptions = []){ + /** + * @var $model self + */ + $model = parent::getById($id); + if($model->isOutdated()){ + $model->loadData($id, $additionalOptions); + } + } + + /** + * checks whether data is outdated and should be refreshed + * @return bool + */ + protected function isOutdated(): bool { + $outdated = true; + if(!$this->dry()){ + $timezone = $this->getF3()->get('getTimeZone')(); + $currentTime = new \DateTime('now', $timezone); + $updateTime = \DateTime::createFromFormat( + 'Y-m-d H:i:s', + $this->updated, + $timezone + ); + $interval = $updateTime->diff($currentTime); + if($interval->days < self::CACHE_MAX_DAYS ){ + $outdated = false; + } + } + return $outdated; + } +} \ No newline at end of file diff --git a/app/main/model/usermodel.php b/app/main/model/usermodel.php index 93e59c84..5f7f8f5b 100644 --- a/app/main/model/usermodel.php +++ b/app/main/model/usermodel.php @@ -12,6 +12,8 @@ use DB\SQL\Schema; use Controller; use Controller\Api\User as User; use Exception; +use lib\Config; +use Lib\Logging; class UserModel extends BasicModel { @@ -28,27 +30,20 @@ class UserModel extends BasicModel { 'type' => Schema::DT_VARCHAR128, 'nullable' => false, 'default' => '', - 'index' => true + 'index' => true, + 'validate' => true ], 'email' => [ 'type' => Schema::DT_VARCHAR128, 'nullable' => false, - 'default' => '' + 'default' => '', + 'validate' => true ], 'userCharacters' => [ 'has-many' => ['Model\UserCharacterModel', 'userId'] ] ]; - protected $validate = [ - 'name' => [ - 'length' => [ - 'min' => 3, - 'max' => 50 - ] - ] - ]; - /** * get all data for this user * -> ! caution ! this function returns sensitive data! (e.g. email,..) @@ -93,23 +88,6 @@ class UserModel extends BasicModel { return $userData; } - /** - * validate and set a email address for this user - * -> empty email is allowed! - * @param string $email - * @return string - */ - public function set_email($email){ - if ( - !empty($email) && - \Audit::instance()->email($email) == false - ) { - // no valid email address - $this->throwValidationError('email'); - } - return $email; - } - /** * check if new user registration is allowed * @param UserModel $self @@ -119,10 +97,8 @@ class UserModel extends BasicModel { */ public function beforeInsertEvent($self, $pkeys){ $registrationStatus = Controller\Controller::getRegistrationStatus(); - switch($registrationStatus){ case 0: - $f3 = self::getF3(); throw new Exception\RegistrationException('User registration is currently not allowed'); break; case 1: @@ -133,6 +109,80 @@ class UserModel extends BasicModel { } } + /** + * @param BasicModel $self + * @param $pkeys + */ + public function afterEraseEvent($self, $pkeys){ + $this->sendDeleteMail(); + } + + /** + * send delete confirm mail to this user + */ + protected function sendDeleteMail(){ + if($this->isMailSendEnabled()){ + $log = new Logging\UserLog('userDelete', $this->getLogChannelData()); + $log->addHandler('mail', 'mail', $this->getSMTPConfig()); + $log->setMessage('Delete Account - {channelName}'); + $log->setData([ + 'message' =>'Your account was successfully deleted.' + ]); + $log->buffer(); + } + } + + /** + * checks whether user has a valid email address and pathfinder has a valid SMTP config + * @return bool + */ + protected function isMailSendEnabled() : bool{ + return Config::isValidSMTPConfig($this->getSMTPConfig()); + } + + /** + * get SMTP config for this user + * @return \stdClass + */ + protected function getSMTPConfig() : \stdClass{ + $config = Config::getSMTPConfig(); + $config->to = $this->email; + return $config; + } + + /** + * validate name column + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_name(string $key, string $val): bool { + $valid = true; + if( + mb_strlen($val) < 3 || + mb_strlen($val) > 80 + ){ + $valid = false; + $this->throwValidationException($key); + } + return $valid; + } + + /** + * validate email column + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_email(string $key, string $val): bool { + $valid = true; + if ( !empty($val) && \Audit::instance()->email($val) == false ){ + $valid = false; + $this->throwValidationException($key); + } + return $valid; + } + /** * check whether this character has already a user assigned to it * @return bool @@ -145,10 +195,10 @@ class UserModel extends BasicModel { /** * search for user by unique username * @param $name - * @return array|FALSE + * @return \DB\Cortex */ public function getByName($name){ - return $this->getByForeignKey('name', $name, [], 0); + return $this->getByForeignKey('name', $name, []); } /** @@ -165,17 +215,9 @@ class UserModel extends BasicModel { if($this->_id === $currentSessionUser['ID']){ // user matches session data - $sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS); - if($characterId > 0){ - // search for specific characterData - foreach($sessionCharacters as $characterData){ - if($characterId === (int)$characterData['ID']){ - $data = $characterData; - break; - } - } - }elseif( !empty($sessionCharacters) ){ + $data = $this->findSessionCharacterData($characterId); + }elseif( !empty($sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS)) ){ // no character was requested ($requestedCharacterId = 0) AND session characters were found // -> get first matched character (e.g. user open browser tab) $data = $sessionCharacters[0]; @@ -206,6 +248,26 @@ class UserModel extends BasicModel { return $data; } + /** + * search in session data for $characterId + * @param int $characterId + * @return array + */ + public function findSessionCharacterData(int $characterId): array{ + $data = []; + if($characterId){ + $sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS); + // search for specific characterData + foreach($sessionCharacters as $characterData){ + if($characterId === (int)$characterData['ID']){ + $data = $characterData; + break; + } + } + } + return $data; + } + /** * get all userCharacters models for a user * characters will be checked/updated on login by CCP API call @@ -292,4 +354,16 @@ class UserModel extends BasicModel { return $activeCharacters; } + /** + * get object relevant data for model log channel + * @return array + */ + public function getLogChannelData() : array{ + return [ + 'channelId' => $this->_id, + 'channelName' => $this->name + ]; + } + + } \ No newline at end of file diff --git a/app/pathfinder.ini b/app/pathfinder.ini index df0dd45b..54063594 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -3,7 +3,7 @@ [PATHFINDER] NAME = Pathfinder ; installed version (used for CSS/JS cache busting) -VERSION = v1.2.5 +VERSION = v1.3.0 ; contact information [optional] CONTACT = https://github.com/exodus4d ; public contact email [optional] @@ -31,6 +31,11 @@ MODE_MAINTENANCE = 0 CORPORATION = ALLIANCE = +; Slack API integration =========================================================================== +[PATHFINDER.SLACK] +; Global Slack API status, check PATHFINDER.MAP section for individual control (0=disabled, 1=enabled) +STATUS = 1 + ; View ============================================================================================ [PATHFINDER.VIEW] ; static page templates @@ -55,29 +60,51 @@ ADMIN = templates/view/admin.html ; - Max number of shared entities per map ; MAX_SYSTEMS: ; - Max number of active systems per map -; ACTIVITY_LOGGING (0: disable, 1: enable): -; - Whether user activity should be logged for a map type -; - E.g. create/update/delete of systems/connections/signatures +; LOG_ACTIVITY_ENABLED (0: disable, 1: enable): +; - Whether user activity statistics can be anabled for a map type +; - E.g. create/update/delete of systems/connections/signatures/... +; LOG_HISTORY_ENABLED (0: disable, 1: enable): +; - Whether map change history should be logged to separat *.log files +; - see: [PATHFINDER.HISTORY] config section below +; SEND_HISTORY_SLACK_ENABLED (0: disable, 1: enable): +; - Send map updates to a Slack channel per map +; SEND_RALLY_SLACK_ENABLED (0: disable, 1: enable): +; - Send rally point pokes to a Slack channel per map +; SEND_RALLY_Mail_ENABLED (0: disable, 1: enable): +; - Send rally point pokes by mail +; - see: [PATHFINDER.NOTIFICATION] section below [PATHFINDER.MAP.PRIVATE] -LIFETIME = 30 -MAX_COUNT = 3 -MAX_SHARED = 10 -MAX_SYSTEMS = 50 -ACTIVITY_LOGGING = 1 +LIFETIME = 60 +MAX_COUNT = 3 +MAX_SHARED = 10 +MAX_SYSTEMS = 50 +LOG_ACTIVITY_ENABLED = 1 +LOG_HISTORY_ENABLED = 1 +SEND_HISTORY_SLACK_ENABLED = 0 +SEND_RALLY_SLACK_ENABLED = 1 +SEND_RALLY_Mail_ENABLED = 0 [PATHFINDER.MAP.CORPORATION] -LIFETIME = 99999 -MAX_COUNT = 3 -MAX_SHARED = 3 -MAX_SYSTEMS = 100 -ACTIVITY_LOGGING = 1 +LIFETIME = 99999 +MAX_COUNT = 3 +MAX_SHARED = 3 +MAX_SYSTEMS = 100 +LOG_ACTIVITY_ENABLED = 1 +LOG_HISTORY_ENABLED = 1 +SEND_HISTORY_SLACK_ENABLED = 1 +SEND_RALLY_SLACK_ENABLED = 1 +SEND_RALLY_Mail_ENABLED = 0 [PATHFINDER.MAP.ALLIANCE] -LIFETIME = 99999 -MAX_COUNT = 3 -MAX_SHARED = 2 -MAX_SYSTEMS = 100 -ACTIVITY_LOGGING = 0 +LIFETIME = 99999 +MAX_COUNT = 3 +MAX_SHARED = 2 +MAX_SYSTEMS = 100 +LOG_ACTIVITY_ENABLED = 0 +LOG_HISTORY_ENABLED = 1 +SEND_HISTORY_SLACK_ENABLED = 1 +SEND_RALLY_SLACK_ENABLED = 1 +SEND_RALLY_Mail_ENABLED = 0 ; Route search ==================================================================================== [PATHFINDER.ROUTE] @@ -87,7 +114,7 @@ SEARCH_DEPTH = 7000 ; default count of routes that will be checked (initial) when a system is selected (default: 2) SEARCH_DEFAULT_COUNT = 2 ; max count of routes that can be selected in "route settings" dialog (default: 4) -MAX_Default_COUNT = 4 +MAX_DEFAULT_COUNT = 4 ; max count of routes that will be checked (MAX_COUNT + custom routes ) (default: 6) LIMIT = 6 @@ -151,8 +178,6 @@ LOGIN = login SESSION_SUSPECT = session_suspect ; account deleted DELETE_ACCOUNT = account_delete -; unauthorized request (HTTP 401) -UNAUTHORIZED = unauthorized ; admin action (e.g. kick, bann) log ADMIN = admin ; TCP socket errors @@ -160,7 +185,15 @@ SOCKET_ERROR = socket_error ; debug log for development DEBUG = debug +[PATHFINDER.HISTORY] +; cache time for parsed log files (seconds) (default: 5) +CACHE = 5 +; file folder for 'history' logs (e.g. map history) (default: history/) +LOG = history/ + ; API ============================================================================================= [PATHFINDER.API] +CCP_IMAGE_SERVER = https://image.eveonline.com +Z_KILLBOARD = https://zkillboard.com/api ; GitHub Developer API GIT_HUB = https://api.github.com diff --git a/app/requirements.ini b/app/requirements.ini index 5d9f6752..f475eede 100644 --- a/app/requirements.ini +++ b/app/requirements.ini @@ -30,7 +30,7 @@ ZMQ = 1.1.3 ; https://pecl.php.net/package/event EVENT = 2.3.0 -; max execution time for requests +; max execution time for requests (seconds) MAX_EXECUTION_TIME = 10 ; max variable size for $_GET, $_POST and $_COOKIE @@ -39,6 +39,9 @@ MAX_EXECUTION_TIME = 10 ; PHP default = 1000 MAX_INPUT_VARS = 3000 +; Formatted HTML StackTraces +HTML_ERRORS = 0 + [REQUIREMENTS.LIBS] ZMQ = 4.1.3 diff --git a/composer-dev.json b/composer-dev.json index 206241ba..a0b84c78 100644 --- a/composer-dev.json +++ b/composer-dev.json @@ -21,8 +21,12 @@ }], "require": { "php-64bit": ">=7.0", - "ext-zmq": "1.1.*", + "ext-curl": ">=7.0", + "ext-zmq": ">=1.1.3", "react/zmq": "0.3.*", + "monolog/monolog": "1.*", + "websoftwares/monolog-zmq-handler": "0.2.*", + "swiftmailer/swiftmailer": "^6.0", "exodus4d/pathfinder_esi": "dev-develop as 0.0.x-dev" } } diff --git a/composer.json b/composer.json index 2c7e48b2..9b7a8358 100644 --- a/composer.json +++ b/composer.json @@ -21,8 +21,12 @@ }], "require": { "php-64bit": ">=7.0", - "ext-zmq": "1.1.*", + "ext-curl": ">=7.0", + "ext-zmq": ">=1.1.3", "react/zmq": "0.3.*", - "exodus4d/pathfinder_esi": "dev-master#v1.1.0" + "monolog/monolog": "1.*", + "websoftwares/monolog-zmq-handler": "0.2.*", + "swiftmailer/swiftmailer": "^6.0", + "exodus4d/pathfinder_esi": "dev-master#v1.2.0" } } diff --git a/gulpfile.js b/gulpfile.js index b57823fd..b39faf7f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -499,7 +499,7 @@ gulp.task('task:hintJS', () => { * concat/build JS files by modules */ gulp.task('task:concatJS', () => { - let modules = ['login', 'mappage', 'setup', 'admin', 'notification']; + let modules = ['login', 'mappage', 'setup', 'admin', 'notification', 'datatables.loader']; let srcModules = ['./js/app/*(' + modules.join('|') + ').js']; return gulp.src(srcModules, {base: 'js'}) @@ -854,11 +854,15 @@ gulp.task( 'production', gulp.series( 'task:configProduction', - 'task:cleanJsBuild', - 'task:cleanCssBuild', gulp.parallel( - 'task:buildJs', - 'task:watchCss' + gulp.series( + 'task:cleanJsBuild', + 'task:buildJs' + ), + gulp.series( + 'task:cleanCssBuild', + 'task:watchCss' + ) ) ) ); diff --git a/js/app.js b/js/app.js index 652d7af6..6683161c 100644 --- a/js/app.js +++ b/js/app.js @@ -11,7 +11,7 @@ requirejs.config({ paths: { layout: 'layout', - config: 'app/config', // path for "configuration" files dir + conf: 'app/conf', // path for "config" files dir dialog: 'app/ui/dialog', // path for "dialog" files dir templates: '../../templates', // template dir img: '../../img', // images dir @@ -59,11 +59,13 @@ requirejs.config({ tweenLite: 'lib/TweenLite.min', // datatables // v1.10.12 DataTables - https://datatables.net + 'datatables.loader': './app/datatables.loader', 'datatables.net': 'lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min', 'datatables.net-buttons': 'lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min', 'datatables.net-buttons-html': 'lib/datatables/Buttons-1.2.1/js/buttons.html5.min', 'datatables.net-responsive': 'lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min', 'datatables.net-select': 'lib/datatables/Select-1.2.0/js/dataTables.select.min', + 'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis', // notification plugin pnotify: 'lib/pnotify/pnotify', // v3.0.0 PNotify - notification core file - https://sciactive.com/pnotify/ @@ -94,6 +96,9 @@ requirejs.config({ customScrollbar: { deps: ['jquery', 'mousewheel'] }, + 'datatables.loader': { + deps: ['jquery'] + }, 'datatables.net': { deps: ['jquery'] }, @@ -109,6 +114,9 @@ requirejs.config({ 'datatables.net-select': { deps: ['datatables.net'] }, + 'datatables.plugins.render.ellipsis': { + deps: ['datatables.net'] + }, xEditable: { deps: ['bootstrap'] }, diff --git a/js/app/admin.js b/js/app/admin.js index aecb732a..18a7c531 100644 --- a/js/app/admin.js +++ b/js/app/admin.js @@ -6,11 +6,7 @@ define([ 'jquery', 'app/init', 'app/util', - 'datatables.net', - 'datatables.net-buttons', - 'datatables.net-buttons-html', - 'datatables.net-responsive', - 'datatables.net-select' + 'datatables.loader' ], function($, Init, Util) { 'use strict'; diff --git a/js/app/config/signature_type.js b/js/app/conf/signature_type.js similarity index 100% rename from js/app/config/signature_type.js rename to js/app/conf/signature_type.js diff --git a/js/app/config/system_effect.js b/js/app/conf/system_effect.js similarity index 100% rename from js/app/config/system_effect.js rename to js/app/conf/system_effect.js diff --git a/js/app/datatables.loader.js b/js/app/datatables.loader.js new file mode 100644 index 00000000..82f451bc --- /dev/null +++ b/js/app/datatables.loader.js @@ -0,0 +1,11 @@ +define([ + 'datatables.net', + 'datatables.net-buttons', + 'datatables.net-buttons-html', + 'datatables.net-responsive', + 'datatables.net-select' +], (a, b) => { + 'use strict'; + + // all Datatables stuff is available... +}); \ No newline at end of file diff --git a/js/app/init.js b/js/app/init.js index 0b0e6f8f..79360978 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -31,6 +31,7 @@ define(['jquery'], function($) { deleteMap: 'api/map/delete', // ajax URL - delete map importMap: 'api/map/import', // ajax URL - import map getMapConnectionData: 'api/map/getConnectionData', // ajax URL - get connection data + getMapLogData: 'api/map/getLogData', // ajax URL - get logs data // system API searchSystem: 'api/system/search', // ajax URL - search system by name saveSystem: 'api/system/save', // ajax URL - saves system to map @@ -38,6 +39,7 @@ define(['jquery'], function($) { getSystemGraphData: 'api/system/graphData', // ajax URL - get all system graph data getConstellationData: 'api/system/constellationData', // ajax URL - get system constellation data setDestination: 'api/system/setDestination', // ajax URL - set destination + pokeRally: 'api/system/pokeRally', // ajax URL - send rally point pokes // connection API saveConnection: 'api/connection/save', // ajax URL - save new connection to map deleteConnection: 'api/connection/delete', // ajax URL - delete connection from map @@ -52,10 +54,6 @@ define(['jquery'], function($) { // GitHub API gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub }, - url: { - ccpImageServer: '//image.eveonline.com/', // CCP image Server - zKillboard: '//zkillboard.com/api/' // killboard api - }, breakpoints: [ { name: 'desktop', width: Infinity }, { name: 'tablet', width: 1200 }, diff --git a/js/app/key.js b/js/app/key.js index 01a04cdb..5e264c3b 100644 --- a/js/app/key.js +++ b/js/app/key.js @@ -63,7 +63,7 @@ define([ }; /** - * enables some console.log() information + * enables some debug output in console * @type {boolean} */ let debug = false; @@ -297,20 +297,29 @@ define([ // global dom remove listener ------------------------------------------------------------------- // -> check whether the removed element had an event listener active and removes them. - document.body.addEventListener ('DOMNodeRemoved', function(e){ - if(typeof e.target.getAttribute === 'function'){ - let eventNames = e.target.getAttribute(dataKeyEvents); - if(eventNames){ - eventNames.split(',').forEach((event) => { - let index = allEvents[event].elements.indexOf(e.target); - if(index > -1){ - // remove element from event list - allEvents[event].elements.splice(index, 1); + new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if(mutation.type === 'childList'){ + for (let i = 0; i < mutation.removedNodes.length; i++){ + let removedNode = mutation.removedNodes[i]; + if(typeof removedNode.getAttribute === 'function'){ + let eventNames = removedNode.getAttribute(dataKeyEvents); + if(eventNames){ + let events = eventNames.split(','); + for(let j = 0; i < events.length; j++){ + let event = events[j]; + let index = allEvents[event].elements.indexOf(removedNode); + if(index > -1){ + // remove element from event list + allEvents[event].elements.splice(index, 1); + } + } + } } - }); + } } - } - }, false); + }); + }).observe(document.body, { childList: true, subtree: true }); isInit = true; } diff --git a/js/app/logging.js b/js/app/logging.js index 61f62e94..0cfc5cf6 100644 --- a/js/app/logging.js +++ b/js/app/logging.js @@ -80,7 +80,7 @@ define([ let showDialog = function(){ // dialog content - requirejs(['text!templates/dialog/task_manager.html', 'mustache'], function(templateTaskManagerDialog, Mustache) { + requirejs(['text!templates/dialog/task_manager.html', 'mustache', 'datatables.loader'], function(templateTaskManagerDialog, Mustache) { let data = { id: config.taskDialogId, dialogDynamicAreaClass: config.dialogDynamicAreaClass, diff --git a/js/app/login.js b/js/app/login.js index 2d189bb4..30ed0997 100644 --- a/js/app/login.js +++ b/js/app/login.js @@ -676,7 +676,8 @@ define([ dataType: 'json', context: { cookieName: requestData.cookie, - characterElement: characterElement + characterElement: characterElement, + browserTabId: Util.getBrowserTabId() } }).done(function(responseData, textStatus, request){ this.characterElement.hideLoadingAnimation(); @@ -698,9 +699,11 @@ define([ let data = { link: this.characterElement.data('href'), cookieName: this.cookieName, + browserTabId: this.browserTabId, character: responseData.character, authLabel: getCharacterAuthLabel(responseData.character.authStatus), - authOK: responseData.character.authStatus === 'OK' + authOK: responseData.character.authStatus === 'OK', + hasActiveSession: responseData.character.hasActiveSession === true }; let content = Mustache.render(template, data); @@ -766,6 +769,12 @@ define([ * main init "landing" page */ $(function(){ + // clear sessionStorage + Util.clearSessionStorage(); + + // set default AJAX config + Util.ajaxSetup(); + // set Dialog default config Util.initDefaultBootboxConfig(); diff --git a/js/app/map/local.js b/js/app/map/local.js index 29bdf747..3468c6fd 100644 --- a/js/app/map/local.js +++ b/js/app/map/local.js @@ -28,9 +28,9 @@ define([ overlayLocalJumpsClass: 'pf-map-overlay-local-jumps', // class for jump distance for table results // dataTable - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells - tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells - tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells + tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells + tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) // toolbar toolbarClass: 'pf-map-overlay-toolbar', // class for toolbar - content @@ -288,236 +288,240 @@ define([ * @returns {*} */ $.fn.initLocalOverlay = function(mapId){ - return this.each(function(){ - let parentElement = $(this); + let parentElements = $(this); - let overlay = $('
', { - class: [config.overlayClass, config.overlayLocalClass].join(' ') - }); + require(['datatables.loader'], () => { + parentElements.each(function(){ + let parentElement = $(this); - let content = $('
', { - class: [ 'text-right', config.overlayLocalContentClass].join(' ') - }); + let overlay = $('
', { + class: [config.overlayClass, config.overlayLocalClass].join(' ') + }); - // crate new route table - let table = $('', { - class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ') - }); + let content = $('
', { + class: [ 'text-right', config.overlayLocalContentClass].join(' ') + }); - let overlayMain = $('
', { - text: '', - class: config.overlayLocalMainClass - }).append( - $('', { - class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ') - }), - $('', { - class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '), - text: 0 - }), - $('
', { - class: config.overlayLocalJumpsClass + // crate new route table + let table = $('
', { + class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ') + }); + + let overlayMain = $('
', { + text: '', + class: config.overlayLocalMainClass + }).append( + $('', { + class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ') + }), + $('', { + class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '), + text: 0 + }), + $('
', { + class: config.overlayLocalJumpsClass + }).append( + $('', { + class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '), + text: MapUtil.config.defaultLocalJumpRadius + }).attr('title', 'jumps') + ) + ); + + let headline = $('
', { + class: config.overlayLocalHeadlineClass }).append( $('', { - class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '), - text: MapUtil.config.defaultLocalJumpRadius - }).attr('title', 'jumps') - ) - ); + html: 'Nearby   ', + class: 'pull-left' + }), + $(''), + $(''), + $('', { + class: ['badge', ' txt-color', 'txt-color-red'].join(' '), + text: 0 + }) + ); - let headline = $('
', { - class: config.overlayLocalHeadlineClass - }).append( - $('', { - html: 'Nearby   ', - class: 'pull-left' - }), - $(''), - $(''), - $('', { - class: ['badge', ' txt-color', 'txt-color-red'].join(' '), - text: 0 - }) - ); + content.append(headline); + content.append(table); + // toolbar not used for now + // content.append(initToolbar()); - content.append(headline); - content.append(table); - // toolbar not used for now - // content.append(initToolbar()); + overlay.append(overlayMain); + overlay.append(content); - overlay.append(overlayMain); - overlay.append(content); + // set observer + setOverlayObserver(overlay, mapId); - // set observer - setOverlayObserver(overlay, mapId); + parentElement.append(overlay); - parentElement.append(overlay); + // init local table --------------------------------------------------------------------------------------- - // init local table --------------------------------------------------------------------------------------- + table.on('draw.dt', function(e, settings){ + // init table tooltips + $(this).find('td').initTooltips({ + container: 'body', + placement: 'left' + }); - table.on('draw.dt', function(e, settings){ - // init table tooltips - $(this).find('td').initTooltips({ - container: 'body', - placement: 'left' - }); - - // hide pagination in case of only one page - let paginationElement = overlay.find('.dataTables_paginate'); - let pageElements = paginationElement.find('span .paginate_button'); - if(pageElements.length <= 1){ - paginationElement.hide(); - }else{ - paginationElement.show(); - } - }); - - // table init complete - table.on( 'init.dt', function (){ - // init table head tooltips - $(this).initTooltips({ - container: 'body', - placement: 'top' - }); - }); - - let localTable = table.DataTable( { - pageLength: 13, // hint: if pagination visible => we need space to show it - paging: true, - lengthChange: false, - ordering: true, - order: [ 0, 'asc' ], - info: false, - searching: false, - hover: false, - autoWidth: false, - rowId: function(rowData) { - return 'pf-local-row_' + rowData.id; // characterId - }, - language: { - emptyTable: 'You are alone' - }, - columnDefs: [ - { - targets: 0, - orderable: true, - title: ' ', - width: '1px', - className: ['pf-help-default', 'text-center'].join(' '), - data: 'jumps', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - if(value === 0){ - value = ''; - } - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.system.name'); - } - },{ - targets: 1, - orderable: false, - title: '', - width: '26px', - className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '), - data: 'log.ship', - render: { - _: function(data, type, row, meta){ - let value = data.typeName; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.ship.typeName'); - } - }, { - targets: 2, - orderable: true, - title: 'ship name', - width: '80px', - data: 'log.ship', - render: { - _: function(data, type, row, meta){ - let value = data.name; - if(type === 'display'){ - value = '
' + data.name + '
'; - } - return value; - }, - sort: 'name' - } - },{ - targets: 3, - orderable: true, - title: 'pilot', - data: 'name', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - value = '
' + data + '
'; - } - return value; - } - } - },{ - targets: 4, - orderable: false, - title: '', - width: '10px', - className: ['pf-help-default'].join(' '), - data: 'log.station', - render: { - _: function(data, type, row, meta){ - let value = ''; - if( - type === 'display' && - data.id - ){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.station.name'); - } - },{ - targets: 5, - orderable: false, - title: '', - width: '10px', - className: [config.tableActionCellClass].join(' '), - data: 'id', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - // open character information window (ingame) - $(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){ - let cellData = e.data.tableApi.cell(this).data(); - Util.openIngameWindow(e.data.cellData); - }); - } + // hide pagination in case of only one page + let paginationElement = overlay.find('.dataTables_paginate'); + let pageElements = paginationElement.find('span .paginate_button'); + if(pageElements.length <= 1){ + paginationElement.hide(); + }else{ + paginationElement.show(); } - ] + }); + + // table init complete + table.on( 'init.dt', function (){ + // init table head tooltips + $(this).initTooltips({ + container: 'body', + placement: 'top' + }); + }); + + let localTable = table.DataTable( { + pageLength: 13, // hint: if pagination visible => we need space to show it + paging: true, + lengthChange: false, + ordering: true, + order: [ 0, 'asc' ], + info: false, + searching: false, + hover: false, + autoWidth: false, + rowId: function(rowData) { + return 'pf-local-row_' + rowData.id; // characterId + }, + language: { + emptyTable: 'You are alone' + }, + columnDefs: [ + { + targets: 0, + orderable: true, + title: ' ', + width: '1px', + className: ['pf-help-default', 'text-center'].join(' '), + data: 'jumps', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + if(value === 0){ + value = ''; + } + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.system.name'); + } + },{ + targets: 1, + orderable: false, + title: '', + width: '26px', + className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '), + data: 'log.ship', + render: { + _: function(data, type, row, meta){ + let value = data.typeName; + if(type === 'display'){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.ship.typeName'); + } + }, { + targets: 2, + orderable: true, + title: 'ship name', + width: '80px', + data: 'log.ship', + render: { + _: function(data, type, row, meta){ + let value = data.name; + if(type === 'display'){ + value = '
' + data.name + '
'; + } + return value; + }, + sort: 'name' + } + },{ + targets: 3, + orderable: true, + title: 'pilot', + data: 'name', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = '
' + data + '
'; + } + return value; + } + } + },{ + targets: 4, + orderable: false, + title: '', + width: '10px', + className: ['pf-help-default'].join(' '), + data: 'log.station', + render: { + _: function(data, type, row, meta){ + let value = ''; + if( + type === 'display' && + data.id + ){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.station.name'); + } + },{ + targets: 5, + orderable: false, + title: '', + width: '10px', + className: [config.tableCellActionClass].join(' '), + data: 'id', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // open character information window (ingame) + $(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){ + let cellData = e.data.tableApi.cell(this).data(); + Util.openIngameWindow(e.data.cellData); + }); + } + } + ] + }); }); }); }; diff --git a/js/app/map/map.js b/js/app/map/map.js index 687dfba3..8cd97831 100644 --- a/js/app/map/map.js +++ b/js/app/map/map.js @@ -945,14 +945,14 @@ define([ mapContainer = $(mapContainer); // add additional information for this map - if(mapContainer.data('updated') !== mapConfig.config.updated){ + if(mapContainer.data('updated') !== mapConfig.config.updated.updated){ mapContainer.data('name', mapConfig.config.name); mapContainer.data('scopeId', mapConfig.config.scope.id); mapContainer.data('typeId', mapConfig.config.type.id); mapContainer.data('typeName', mapConfig.config.type.name); mapContainer.data('icon', mapConfig.config.icon); - mapContainer.data('created', mapConfig.config.created); - mapContainer.data('updated', mapConfig.config.updated); + mapContainer.data('created', mapConfig.config.created.created); + mapContainer.data('updated', mapConfig.config.updated.updated); } // get map data @@ -1320,11 +1320,10 @@ define([ * @returns {boolean} */ let isValidSystem = function(systemData){ - let isValid = true; if( - ! systemData.hasOwnProperty('name') || + !systemData.hasOwnProperty('name') || systemData.name.length === 0 ){ return false; @@ -1382,37 +1381,51 @@ define([ /** * save a new system and add it to the map - * @param map * @param requestData - * @param sourceSystem - * @param callback + * @param context */ - let saveSystem = function(map, requestData, sourceSystem, callback){ + let saveSystem = function(requestData, context){ $.ajax({ type: 'POST', url: Init.path.saveSystem, data: requestData, dataType: 'json', - context: { - map: map, - sourceSystem: sourceSystem + context: context + }).done(function(responseData){ + let newSystemData = responseData.systemData; + + if( !$.isEmptyObject(newSystemData) ){ + Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); + + // draw new system to map + drawSystem(this.map, newSystemData, this.sourceSystem); + + // re/arrange systems (prevent overlapping) + MagnetizerWrapper.setElements(this.map); + + if(this.onSuccess){ + this.onSuccess(); + } } - }).done(function(newSystemData){ - Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); - // draw new system to map - drawSystem(this.map, newSystemData, this.sourceSystem); - - // re/arrange systems (prevent overlapping) - MagnetizerWrapper.setElements(this.map); - - if(callback){ - callback(); + // show errors + if( + responseData.error && + responseData.error.length > 0 + ){ + for(let i = 0; i < responseData.error.length; i++){ + let error = responseData.error[i]; + Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); + } } }).fail(function( jqXHR, status, error) { let reason = status + ' ' + error; Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); + }).always(function(){ + if(this.onAlways){ + this.onAlways(this); + } }); }; @@ -1542,8 +1555,18 @@ define([ } }; - saveSystem(map, requestData, sourceSystem, function(){ - bootbox.hideAll(); + this.find('.modal-content').showLoadingAnimation(); + + saveSystem(requestData, { + map: map, + sourceSystem: sourceSystem, + systemDialog: this, + onSuccess: () => { + bootbox.hideAll(); + }, + onAlways: (context) => { + context.systemDialog.find('.modal-content').hideLoadingAnimation(); + } }); return false; } @@ -1694,7 +1717,8 @@ define([ mapId: mapId, oldConnectionData: connectionData } - }).done(function(newConnectionData){ + }).done(function(responseData){ + let newConnectionData = responseData.connectionData; if( !$.isEmptyObject(newConnectionData) ){ let updateCon = false; @@ -1739,6 +1763,16 @@ define([ this.map.detach(this.connection, {fireEvent: false}); } + // show errors + if( + responseData.error && + responseData.error.length > 0 + ){ + for(let i = 0; i < responseData.error.length; i++){ + let error = responseData.error[i]; + Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); + } + } }).fail(function( jqXHR, status, error) { // remove this connection from map this.map.detach(this.connection, {fireEvent: false}); @@ -1826,7 +1860,6 @@ define([ let moduleData = { id: config.mapContextMenuId, items: [ - {icon: 'fa-street-view', action: 'info', text: 'information'}, {icon: 'fa-plus', action: 'add_system', text: 'add system'}, {icon: 'fa-object-ungroup', action: 'select_all', text: 'select all'}, {icon: 'fa-filter', action: 'filter_scope', text: 'filter scope', subitems: [ @@ -1834,6 +1867,10 @@ define([ {subIcon: '', subAction: 'filter_stargate', subText: 'stargate'}, {subIcon: '', subAction: 'filter_jumpbridge', subText: 'jumpbridge'} ]}, + {icon: 'fa-sitemap', action: 'map', text: 'map', subitems: [ + {subIcon: 'fa-edit', subAction: 'map_edit', subText: 'edit map'}, + {subIcon: 'fa-street-view', subAction: 'map_info', subText: 'map info'}, + ]}, {divider: true, action: 'delete_systems'}, {icon: 'fa-trash', action: 'delete_systems', text: 'delete systems'} ] @@ -1905,7 +1942,7 @@ define([ items: [ {icon: 'fa-plus', action: 'add_system', text: 'add system'}, {icon: 'fa-lock', action: 'lock_system', text: 'lock system'}, - {icon: 'fa-users', action: 'set_rally', text: 'set rally point'}, + {icon: 'fa-volume-up', action: 'set_rally', text: 'set rally point'}, {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'}, @@ -2731,8 +2768,12 @@ define([ let selectedSystems = currentMapElement.getSelectedSystems(); $.fn.showDeleteSystemDialog(currentMap, selectedSystems); break; - case 'info': - // open map info dialog + case 'map_edit': + // open map edit dialog tab + $(document).triggerMenuEvent('ShowMapSettings', {tab: 'edit'}); + break; + case 'map_info': + // open map info dialog tab $(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'}); break; @@ -2893,37 +2934,38 @@ define([ let mapElement = $(this); let mapOverlay = mapElement.getMapOverlay('local'); - let currentCharacterLog = Util.getCurrentCharacterLog(); - let currentMapData = Util.getCurrentMapData(userData.config.id); - let clearLocal = true; + if(userData && userData.config && userData.config.id){ + let currentMapData = Util.getCurrentMapData(userData.config.id); + let currentCharacterLog = Util.getCurrentCharacterLog(); + let clearLocal = true; - if( - currentMapData && - currentCharacterLog && - currentCharacterLog.system - ){ - let currentSystemData = currentMapData.data.systems.filter(function (system) { - return system.systemId === currentCharacterLog.system.id; - }); + if( + currentMapData && + currentCharacterLog && + currentCharacterLog.system + ){ + let currentSystemData = currentMapData.data.systems.filter(function (system) { + return system.systemId === currentCharacterLog.system.id; + }); - if(currentSystemData.length){ - // current user system is on this map - currentSystemData = currentSystemData[0]; + if(currentSystemData.length){ + // current user system is on this map + currentSystemData = currentSystemData[0]; - // check for active users "nearby" (x jumps radius) - let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius); - let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems); + // check for active users "nearby" (x jumps radius) + let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius); + let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems); - // update "local" table in overlay - mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData); - clearLocal = false; + // update "local" table in overlay + mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData); + clearLocal = false; + } + } + + if(clearLocal){ + mapOverlay.clearLocalTable(); } } - - if(clearLocal){ - mapOverlay.clearLocalTable(); - } - }); }; diff --git a/js/app/map/system.js b/js/app/map/system.js index 2b149f93..b5dac0ff 100644 --- a/js/app/map/system.js +++ b/js/app/map/system.js @@ -18,7 +18,19 @@ define([ y: 0 }, - systemActiveClass: 'pf-system-active' // class for an active system in a map + systemActiveClass: 'pf-system-active', // class for an active system in a map + + dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog + + dialogRallyPokeDesktopId: 'pf-rally-dialog-poke-desktop', // id for "desktop" poke checkbox + dialogRallyPokeSlackId: 'pf-rally-dialog-poke-slack', // id for "Slack" poke checkbox + dialogRallyPokeMailId: 'pf-rally-dialog-poke-mail', // id for "mail" poke checkbox + dialogRallyMessageId: 'pf-rally-dialog-message', // id for "message" textarea + + dialogRallyMessageDefault: '' + + 'I need some help!\n\n' + + '- Potential PvP options around\n' + + '- DPS and Logistic ships needed' }; /** @@ -26,41 +38,105 @@ define([ * @param system */ $.fn.showRallyPointDialog = (system) => { + let mapData = Util.getCurrentMapData(system.data('mapid')); + requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) { + + let setCheckboxObserver = (checkboxes) => { + checkboxes.each(function(){ + $(this).on('change', function(){ + // check all others + let allUnchecked = true; + checkboxes.each(function(){ + if(this.checked){ + allUnchecked = false; + } + }); + let textareaElement = $('#' + config.dialogRallyMessageId); + if(allUnchecked){ + textareaElement.prop('disabled', true); + }else{ + textareaElement.prop('disabled', false); + } + }); + }); + }; + + let sendPoke = (requestData, context) => { + // lock dialog + let dialogContent = context.rallyDialog.find('.modal-content'); + dialogContent.showLoadingAnimation(); + + $.ajax({ + type: 'POST', + url: Init.path.pokeRally, + data: requestData, + dataType: 'json', + context: context + }).done(function(data){ + + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': sendPoke', text: reason, type: 'warning'}); + }).always(function(){ + this.rallyDialog.find('.modal-content').hideLoadingAnimation(); + }); + }; + let data = { - notificationStatus: Init.notificationStatus.rallySet + id: config.dialogRallyId, + + dialogRallyPokeDesktopId: config.dialogRallyPokeDesktopId, + dialogRallyPokeSlackId: config.dialogRallyPokeSlackId, + dialogRallyPokeMailId: config.dialogRallyPokeMailId, + dialogRallyMessageId: config.dialogRallyMessageId , + + desktopRallyEnabled: true, + slackRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.slackRally')), + mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')), + dialogRallyMessageDefault: config.dialogRallyMessageDefault, + + systemId: system.data('id') }; let content = Mustache.render(template, data); let rallyDialog = bootbox.dialog({ message: content, - title: 'Set rally point for "' + system.getSystemInfo( ['alias'] ) + '"', + title: 'Set rally point in "' + system.getSystemInfo( ['alias'] ) + '"', buttons: { close: { label: 'cancel', className: 'btn-default' }, - setRallyPoke: { - label: ' set rally and poke', - className: 'btn-primary', - callback: function() { - system.setSystemRally(1, { - poke: true - }); - system.markAsChanged(); - } - }, success: { - label: ' set rally', + label: ' set rally point', className: 'btn-success', callback: function() { - system.setSystemRally(1); + let form = $('#' + config.dialogRallyId).find('form'); + // get form data + let formData = form.getFormValues(); + + // update map + system.setSystemRally(1, { + poke: Boolean(formData.pokeDesktop) + }); system.markAsChanged(); + + // send poke data to server + sendPoke(formData, { + rallyDialog: this + }); } } } }); + + // after modal is shown ================================================================================== + rallyDialog.on('shown.bs.modal', function(e){ + // set event for checkboxes + setCheckboxObserver(rallyDialog.find(':checkbox')); + }); }); }; diff --git a/js/app/mappage.js b/js/app/mappage.js index a856c0a6..34a0d29b 100644 --- a/js/app/mappage.js +++ b/js/app/mappage.js @@ -23,6 +23,9 @@ define([ $(() => { Util.initPrototypes(); + // clear sessionStorage + //Util.clearSessionStorage(); + // set default AJAX config Util.ajaxSetup(); @@ -63,7 +66,8 @@ define([ Init.systemType = initData.systemType; Init.characterStatus = initData.characterStatus; Init.routes = initData.routes; - Init.notificationStatus = initData.notificationStatus; + Init.url = initData.url; + Init.slack = initData.slack; Init.routeSearch = initData.routeSearch; Init.programMode = initData.programMode; @@ -165,8 +169,7 @@ define([ if(jqXHR.responseJSON){ // handle JSON - let errorObj = $.parseJSON(jqXHR.responseText); - + let errorObj = jqXHR.responseJSON; if( errorObj.error && errorObj.error.length > 0 @@ -182,7 +185,6 @@ define([ } $(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData}); - }; /** diff --git a/js/app/module_map.js b/js/app/module_map.js index 59fad3fc..1337d183 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -9,12 +9,7 @@ define([ 'app/ui/system_graph', 'app/ui/system_signature', 'app/ui/system_route', - 'app/ui/system_killboard', - 'datatables.net', - 'datatables.net-buttons', - 'datatables.net-buttons-html', - 'datatables.net-responsive', - 'datatables.net-select' + 'app/ui/system_killboard' ], function($, Init, Util, Map, MapUtil) { 'use strict'; @@ -106,29 +101,31 @@ define([ * @param tabContentElement */ let drawSystemModules = function(tabContentElement){ - let currentSystemData = Util.getCurrentSystemData(); + require(['datatables.loader'], () => { + let currentSystemData = Util.getCurrentSystemData(); - // get Table cell for system Info - let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst); - let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond); + // get Table cell for system Info + let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst); + let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond); - // draw system info module - firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData); + // draw system info module + firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system graph module - firstCell.drawSystemGraphModule(currentSystemData.systemData); + // draw system graph module + firstCell.drawSystemGraphModule(currentSystemData.systemData); - // draw signature table module - firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData); + // draw signature table module + firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system routes module - secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData); + // draw system routes module + secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system killboard module - secondCell.drawSystemKillboardModule(currentSystemData.systemData); + // draw system killboard module + secondCell.drawSystemKillboardModule(currentSystemData.systemData); - // set Module Observer - setModuleObserver(); + // set Module Observer + setModuleObserver(); + }); }; /** @@ -295,7 +292,12 @@ define([ let tabElement = $(this); // set "main" data - tabElement.data('map-id', options.id).data('updated', options.updated); + tabElement.data('map-id', options.id); + + // add updated timestamp (not available for "add" tab + if(Util.getObjVal(options, 'updated.updated')){ + tabElement.data('updated', options.updated.updated); + } // change "tab" link tabElement.attr('href', '#' + config.mapTabIdPrefix + options.id); @@ -565,7 +567,7 @@ define([ activeMapIds.push(mapId); // check for map data change and update tab - if(tabMapData.config.updated > tabElement.data('updated')){ + if(tabMapData.config.updated.updated > tabElement.data('updated')){ tabElement.updateTabData(tabMapData.config); } }else{ diff --git a/js/app/page.js b/js/app/page.js index 1a7613af..bfc863f9 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -198,8 +198,7 @@ define([ getMenuHeadline('Information') ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Statistics').prepend( $('',{ class: 'fa fa-line-chart fa-fw' @@ -209,8 +208,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Effect info').prepend( $('',{ class: 'fa fa-crosshairs fa-fw' @@ -220,8 +218,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Jump info').prepend( $('',{ class: 'fa fa-space-shuttle fa-fw' @@ -233,8 +230,7 @@ define([ getMenuHeadline('Settings') ).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Account').prepend( $('',{ class: 'fa fa-user fa-fw' @@ -245,8 +241,7 @@ define([ ).append( $('', { class: 'list-group-item hide', // trigger by js - id: Util.config.menuButtonFullScreenId, - href: '#' + id: Util.config.menuButtonFullScreenId }).html('  Full screen').prepend( $('',{ class: 'glyphicon glyphicon-fullscreen', @@ -265,8 +260,7 @@ define([ }) ).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Notification test').prepend( $('',{ class: 'fa fa-volume-up fa-fw' @@ -278,8 +272,7 @@ define([ getMenuHeadline('Danger zone') ).append( $('', { - class: 'list-group-item list-group-item-danger', - href: '#' + class: 'list-group-item list-group-item-danger' }).html('  Delete account').prepend( $('',{ class: 'fa fa-user-times fa-fw' @@ -289,8 +282,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-warning', - href: '#' + class: 'list-group-item list-group-item-warning' }).html('  Logout').prepend( $('',{ class: 'fa fa-sign-in fa-fw' @@ -317,8 +309,7 @@ define([ class: 'list-group' }).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Information').prepend( $('',{ class: 'fa fa-street-view fa-fw' @@ -327,12 +318,11 @@ define([ $(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'}); }) ).append( - getMenuHeadline('Settings') + getMenuHeadline('Configuration') ).append( $('', { - class: 'list-group-item', - href: '#' - }).html('  Configuration').prepend( + class: 'list-group-item' + }).html('  Settings').prepend( $('',{ class: 'fa fa-gears fa-fw' }) @@ -342,8 +332,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonGridId, - href: '#' + id: Util.config.menuButtonGridId }).html('   Grid snapping').prepend( $('',{ class: 'glyphicon glyphicon-th' @@ -357,8 +346,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonMagnetizerId, - href: '#' + id: Util.config.menuButtonMagnetizerId }).html('   Magnetizing').prepend( $('',{ class: 'fa fa-magnet fa-fw' @@ -372,8 +360,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonEndpointId, - href: '#' + id: Util.config.menuButtonEndpointId }).html('   Signatures').prepend( $('',{ class: 'fa fa-link fa-fw' @@ -388,8 +375,7 @@ define([ getMenuHeadline('Help') ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Manual').prepend( $('',{ class: 'fa fa-book fa-fw' @@ -399,8 +385,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Shortcuts').prepend( $('',{ class: 'fa fa-keyboard-o fa-fw' @@ -410,8 +395,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Task-Manager').prepend( $('',{ class: 'fa fa-tasks fa-fw' @@ -423,8 +407,7 @@ define([ getMenuHeadline('Danger zone') ).append( $('', { - class: 'list-group-item list-group-item-danger', - href: '#' + class: 'list-group-item list-group-item-danger' }).html('  Delete map').prepend( $('',{ class: 'fa fa-trash fa-fw' @@ -482,11 +465,13 @@ define([ }); // main menus - $('.' + config.headMenuClass).on('click', function() { + $('.' + config.headMenuClass).on('click', function(e) { + e.preventDefault(); slideMenu.slidebars.toggle('left'); }); - $('.' + config.headMapClass).on('click', function() { + $('.' + config.headMapClass).on('click', function(e) { + e.preventDefault(); slideMenu.slidebars.toggle('right'); }); @@ -872,7 +857,7 @@ define([ animateHeaderElement(userInfoElement, function(){ if(currentCharacterChanged){ userInfoElement.find('span').text( newCharacterName ); - userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' ); + userInfoElement.find('img').attr('src', Init.url.ccpImageServer + '/Character/' + newCharacterId + '_32.jpg' ); } // init "character switch" popover userInfoElement.initCharacterSwitchPopover(userData); @@ -894,7 +879,7 @@ define([ // toggle element animateHeaderElement(userShipElement, function(){ userShipElement.find('span').text( newShipName ); - userShipElement.find('img').attr('src', Init.url.ccpImageServer + 'Render/' + newShipId + '_32.png' ); + userShipElement.find('img').attr('src', Init.url.ccpImageServer + '/Render/' + newShipId + '_32.png' ); }, showShipElement); // set new id for next check @@ -1104,10 +1089,12 @@ define([ if( statusElement.data('status') !== status ){ // status has changed if(! programStatusInterval){ + // check if timer exists if not -> set default (in case of the "init" ajax call failed + let programStatusVisible = Init.timer ? Init.timer.PROGRAM_STATUS_VISIBLE : 5000; let timer = function(){ // change status on first timer iteration - if(programStatusCounter === Init.timer.PROGRAM_STATUS_VISIBLE){ + if(programStatusCounter === programStatusVisible){ statusElement.velocity('stop').velocity('fadeOut', { duration: Init.animationSpeed.headerLink, @@ -1134,8 +1121,8 @@ define([ } }; - if(! programStatusInterval){ - programStatusCounter = Init.timer.PROGRAM_STATUS_VISIBLE; + if(!programStatusInterval){ + programStatusCounter = programStatusVisible; programStatusInterval = setInterval(timer, 1000); } } diff --git a/js/app/render.js b/js/app/render.js index 66676088..bbe43ca5 100644 --- a/js/app/render.js +++ b/js/app/render.js @@ -11,7 +11,7 @@ define(['jquery', 'mustache'], function($, Mustache) { * @param functionName * @param config */ - var initModule = function(functionName, config){ + let initModule = function(functionName, config){ if( typeof config.functions === 'object' && @@ -26,7 +26,7 @@ define(['jquery', 'mustache'], function($, Mustache) { * @param config * @param data */ - var showModule = function(config, data){ + let showModule = function(config, data){ // require module template requirejs(['text!templates/' + config.name + '.html'], function(template) { @@ -37,7 +37,7 @@ define(['jquery', 'mustache'], function($, Mustache) { $('#' + data.id).length === 0 ){ - var content = Mustache.render(template, data); + let content = Mustache.render(template, data); // display module switch(config.link){ @@ -62,8 +62,130 @@ define(['jquery', 'mustache'], function($, Mustache) { }); }; + /** + * convert JSON object into HTML highlighted string + * @param obj + */ + let highlightJson = (obj) => { + let multiplyString = (num, str) => { + let sb = []; + for (let i = 0; i < num; i++) { + sb.push(str); + } + return sb.join(''); + }; + + let dateObj = new Date(); + let regexpObj = new RegExp(); + let tab = multiplyString(1, ' '); + let isCollapsible = true; + let quoteKeys = false; + let expImageClicked = '(() => {let container=this.parentNode.nextSibling; container.style.display=container.style.display===\'none\'?\'inline\':\'none\'})();'; + + let checkForArray = function (obj) { + return obj && + typeof obj === 'object' && + typeof obj.length === 'number' && + !(obj.propertyIsEnumerable('length')); + }; + + let getRow = function (indent, data, isPropertyContent) { + let tabs = ''; + for (let i = 0; i < indent && !isPropertyContent; i++) tabs += tab; + if (data !== null && data.length > 0 && data.charAt(data.length - 1) !== '\n') + data = data + '\n'; + return tabs + data; + }; + + let formatLiteral = function (literal, quote, comma, indent, isArray, style) { + if (typeof literal === 'string') + literal = literal.split('<').join('<').split('>').join('>'); + let str = '' + quote + literal + quote + comma + ''; + if (isArray) str = getRow(indent, str); + return str; + }; + + let formatFunction = function (indent, obj) { + let tabs = ''; + for (let i = 0; i < indent; i++) tabs += tab; + let funcStrArray = obj.toString().split('\n'); + let str = ''; + for (let i = 0; i < funcStrArray.length; i++) { + str += ((i === 0) ? '' : tabs) + funcStrArray[i] + '\n'; + } + return str; + }; + + + let highlight = (obj, indent, addComma, isArray, isPropertyContent) => { + let html = ''; + + let comma = (addComma) ? ', ' : ''; + let type = typeof obj; + let clpsHtml = ''; + if (checkForArray(obj)) { + if (obj.length === 0) { + html += getRow(indent, '[ ]' + comma, isPropertyContent); + } else { + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, '[' + clpsHtml, isPropertyContent); + for (let i = 0; i < obj.length; i++) { + html += highlight(obj[i], indent + 1, i < (obj.length - 1), true, false); + } + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, clpsHtml + ']' + comma); + } + } else if (type === 'object') { + if (obj === null) { + html += formatLiteral('null', '', comma, indent, isArray, 'pf-code-Null'); + } else if (obj.constructor === dateObj.constructor) { + html += formatLiteral('new Date(' + obj.getTime() + ') /*' + obj.toLocaleString() + '*/', '', comma, indent, isArray, 'Date'); + } else if (obj.constructor === regexpObj.constructor) { + html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp'); + } else { + let numProps = 0; + for (let prop in obj) numProps++; + if (numProps === 0) { + html += getRow(indent, '{ }' + comma, isPropertyContent); + } else { + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, '{' + clpsHtml, isPropertyContent); + let j = 0; + for (let prop in obj) { + if (obj.hasOwnProperty(prop)) { + let quote = quoteKeys ? '"' : ''; + html += getRow(indent + 1, '' + quote + prop + quote + ': ' + highlight(obj[prop], indent + 1, ++j < numProps, false, true)); + } + } + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, clpsHtml + '}' + comma); + } + } + } else if (type === 'number') { + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Number'); + } else if (type === 'boolean') { + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Boolean'); + } else if (type === 'function') { + if (obj.constructor === regexpObj.constructor) { + html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp'); + } else { + obj = formatFunction(indent, obj); + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Function'); + } + } else if (type === 'undefined') { + html += formatLiteral('undefined', '', comma, indent, isArray, 'pf-code-Null'); + } else { + html += formatLiteral(obj.toString().split('\\').join('\\\\').split('"').join('\\"'), '"', comma, indent, isArray, 'pf-code-String'); + } + + return html; + }; + + return highlight(obj, 0, false, false, false); + }; return { - showModule: showModule + showModule: showModule, + highlightJson: highlightJson }; }); \ No newline at end of file diff --git a/js/app/ui/dialog/map_info.js b/js/app/ui/dialog/map_info.js index 112ace43..fae0f9fb 100644 --- a/js/app/ui/dialog/map_info.js +++ b/js/app/ui/dialog/map_info.js @@ -6,9 +6,10 @@ define([ 'jquery', 'app/init', 'app/util', + 'app/render', 'bootbox', 'app/map/util' -], function($, Init, Util, bootbox, MapUtil) { +], function($, Init, Util, Render, bootbox, MapUtil) { 'use strict'; @@ -19,23 +20,27 @@ define([ // map info dialog/tabs dialogMapInfoSummaryId: 'pf-map-info-dialog-summary', // id for map "summary" container dialogMapInfoUsersId: 'pf-map-info-dialog-users', // id for map "user" container + dialogMapInfoLogsId: 'pf-map-info-dialog-logs', // id for map "logs" container dialogMapInfoRefreshId: 'pf-map-info-dialog-refresh', // id for map "refresh" container - // "summary" container + // dialog containers mapInfoId: 'pf-map-info', // id for map info mapInfoSystemsId: 'pf-map-info-systems', // id for map info systems box mapInfoConnectionsId: 'pf-map-info-connections', // id for map info connections box mapInfoUsersId: 'pf-map-info-users', // id for map info users box + mapInfoLogsId: 'pf-map-info-logs', // id for map info logs box - mapInfoTableClass: 'pf-map-info-table', // class for data mapInfoLifetimeCounterClass: 'pf-map-info-lifetime-counter', // class for map lifetime counter // dataTable - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells - tableImageSmallCellClass: 'pf-table-image-small-cell', // class for table "small image" cells - tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells - tableCounterCellClass: 'pf-table-counter-cell', // class for table "counter" cells - tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) + tableToolsClass: 'pf-table-tools', // class for table "tools" section (e.g. Buttons) + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells + tableCellImageSmallClass: 'pf-table-image-small-cell', // class for table "small image" cells + tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells + tableCellLinkClass: 'pf-table-link-cell', // class for table "links" cells + tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells + tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells + tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) loadingOptions: { // config for loading overlay icon: { @@ -56,16 +61,26 @@ define([ btnOkIcon: 'fa fa-fw fa-close' }; + /** + * get image that marks a table cell as clickable + * @returns {string} + */ + let getIconForInformationWindow = () => { + return ''; + }; + /** * loads the map info data into an element * @param mapData */ - $.fn.loadMapInfoData = function(mapData){ - let mapElement = $(this); + $.fn.initMapInfoData = function(mapData){ + let mapElement = $(this).empty(); - mapElement.empty(); mapElement.showLoadingAnimation(config.loadingOptions); + // get some more config values from this map. Which are not part of "mapData" + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + let countSystems = mapData.data.systems.length; let countConnections = mapData.data.connections.length; @@ -80,11 +95,11 @@ define([ } } - // check max map limits (e.g. max systems per map) ============================================================ + // check max map limits (e.g. max systems per map) ------------------------------------------------------------ let percentageSystems = (100 / mapType.defaultConfig.max_systems) * countSystems; let maxSystemsClass = (percentageSystems < 90) ? 'txt-color-success' : (percentageSystems < 100) ? 'txt-color-warning' : 'txt-color-danger'; - // build content ============================================================================================== + // build content ---------------------------------------------------------------------------------------------- let dlElementLeft = $('
', { class: 'dl-horizontal', @@ -115,13 +130,6 @@ define([ class: 'dl-horizontal', css: {'float': 'right'} }).append( - $('
').text( 'Lifetime' ) - ).append( - $('
', { - class: config.mapInfoLifetimeCounterClass, - text: mapData.config.created - }) - ).append( $('
').text( 'Systems' ) ).append( $('
', { @@ -131,6 +139,17 @@ define([ $('
').text( 'Connections' ) ).append( $('
').text( countConnections ) + ).append( + $('
').text( 'Lifetime' ) + ).append( + $('
', { + class: config.mapInfoLifetimeCounterClass, + text: mapData.config.created + }) + ).append( + $('
').text( 'Created' ) + ).append( + $('
').text( mapDataOrigin.config.created.character.name) ); mapElement.append(dlElementRight); @@ -145,14 +164,11 @@ define([ * loads system info table into an element * @param mapData */ - $.fn.loadSystemInfoTable = function(mapData){ - - let systemsElement = $(this); - - systemsElement.empty(); + $.fn.initSystemInfoTable = function(mapData){ + let systemsElement = $(this).empty(); let systemTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); systemsElement.append(systemTable); @@ -292,7 +308,7 @@ define([ systemsData.push(tempData); } - let systemsDataTable = systemTable.dataTable( { + let systemsDataTable = systemTable.DataTable( { pageLength: 20, paging: true, lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']], @@ -343,7 +359,14 @@ define([ } },{ title: 'system', - data: 'name' + data: 'name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.id }); + }); + } },{ title: 'alias', data: 'alias' @@ -401,7 +424,7 @@ define([ title: 'updated', width: '80px', searchable: false, - className: ['text-right', config.tableCounterCellClass, 'min-desktop'].join(' '), + className: ['text-right', config.tableCellCounterClass, 'min-desktop'].join(' '), data: 'updated', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter(); @@ -418,7 +441,7 @@ define([ orderable: false, searchable: false, width: '10px', - className: ['text-center', config.tableActionCellClass].join(' '), + className: ['text-center', config.tableCellActionClass].join(' '), data: 'clear', createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { let tempTableElement = this; @@ -444,11 +467,11 @@ define([ Util.showNotify({title: 'System deleted', text: rowData.name, type: 'success'}); - // refresh connection table (connections might have changed) ================== + // refresh connection table (connections might have changed) -------------- let connectionsElement = $('#' + config.mapInfoConnectionsId); let mapDataNew = activeMap.getMapDataFromClient({forceData: true}); - connectionsElement.loadConnectionInfoTable(mapDataNew); + connectionsElement.initConnectionInfoTable(mapDataNew); }else{ // error Util.showNotify({title: 'Failed to delete system', text: rowData.name, type: 'error'}); @@ -472,13 +495,11 @@ define([ * loads connection info table into an element * @param mapData */ - $.fn.loadConnectionInfoTable = function(mapData){ - let connectionsElement = $(this); - - connectionsElement.empty(); + $.fn.initConnectionInfoTable = function(mapData){ + let connectionsElement = $(this).empty(); let connectionTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); connectionsElement.append(connectionTable); @@ -489,7 +510,7 @@ define([ connectionsElement.hideLoadingAnimation(); }); - // connections table ========================================================================================== + // connections table ------------------------------------------------------------------------------------------ // prepare data for dataTables let connectionData = []; @@ -505,8 +526,10 @@ define([ scope_sort: tempConnectionData.scope }; - // source system name - tempConData.source = tempConnectionData.sourceName; + tempConData.source = { + id: tempConnectionData.source, + name: tempConnectionData.sourceName, + }; // connection let connectionClasses = []; @@ -519,8 +542,10 @@ define([ tempConData.connection = '
'; - - tempConData.target = tempConnectionData.targetName; + tempConData.target = { + id: tempConnectionData.target, + name: tempConnectionData.targetName, + }; tempConData.updated = tempConnectionData.updated; @@ -557,7 +582,14 @@ define([ } },{ title: 'source system', - data: 'source' + data: 'source.name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.source.id }); + }); + } },{ title: 'connection', width: '80px', @@ -567,12 +599,19 @@ define([ data: 'connection' }, { title: 'target system', - data: 'target' + data: 'target.name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.target.id }); + }); + } },{ title: 'updated', width: '80px', searchable: false, - className: ['text-right', config.tableCounterCellClass].join(' '), + className: ['text-right', config.tableCellCounterClass].join(' '), data: 'updated', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter(); @@ -589,7 +628,7 @@ define([ orderable: false, searchable: false, width: '10px', - className: ['text-center', config.tableActionCellClass].join(' '), + className: ['text-center', config.tableCellActionClass].join(' '), data: 'clear', createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { let tempTableElement = this; @@ -621,13 +660,11 @@ define([ * loads user info table into an element * @param mapData */ - $.fn.loadUsersInfoTable = function(mapData){ - let usersElement = $(this); - - usersElement.empty(); + $.fn.initUsersInfoTable = function(mapData){ + let usersElement = $(this).empty(); let userTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); usersElement.append(userTable); @@ -644,11 +681,7 @@ define([ }); }); - let getIconForInformationWindow = () => { - return ''; - }; - - // users table ================================================================================================ + // users table ------------------------------------------------------------------------------------------------ // prepare users data for dataTables let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id ); let usersData = []; @@ -688,13 +721,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '), + className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '), data: 'log.ship', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -721,13 +754,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: [config.tableImageCellClass].join(' '), + className: [config.tableCellImageClass].join(' '), data: 'id', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -737,7 +770,7 @@ define([ title: 'pilot', orderable: true, searchable: true, - className: [config.tableActionCellClass].join(' '), + className: [config.tableCellActionClass].join(' '), data: 'name', render: { _: function(data, type, row, meta){ @@ -761,13 +794,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: [config.tableImageCellClass, config.tableImageSmallCellClass].join(' '), + className: [config.tableCellImageClass, config.tableCellImageSmallClass].join(' '), data: 'corporation', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -777,7 +810,7 @@ define([ title: 'corporation', orderable: true, searchable: true, - className: [config.tableActionCellClass].join(' '), + className: [config.tableCellActionClass].join(' '), data: 'corporation', render: { _: function (data, type, row, meta) { @@ -821,6 +854,316 @@ define([ }; + /** + * loads logs table into an element + * @param mapData + */ + $.fn.initLogsInfoTable = function(mapData){ + let logsElement = $(this).empty(); + + /** + * ajax load function for log fdata + * @param requestData + * @param context + */ + let getLogsData = (requestData, context) => { + context.logsElement.showLoadingAnimation(config.loadingOptions); + + $.ajax({ + type: 'POST', + url: Init.path.getMapLogData, + data: requestData, + dataType: 'json', + context: context + }).done(function(data){ + this.callback(data, context); + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': loadLogs', text: reason, type: 'warning'}); + }).always(function(){ + this.logsElement.hideLoadingAnimation(); + }); + }; + + /** + * callback function after ajax response with log data + * @param responseData + * @param context + */ + let updateTableDataCallback = (responseData, context) => { + let newLogCount = responseData.data.length; + + if(newLogCount > 0){ + let pageInfoOld = context.tableApi.page.info(); + + // add new rows + context.tableApi.rows.add(responseData.data).draw(); + + let newPageIndex = 0; + if(pageInfoOld.recordsDisplay === 0){ + Util.showNotify({title: 'New logs loaded', text: newLogCount + ' most recent logs added', type: 'success'}); + }else{ + // get new pageInfo (new max page count) + let pageInfoNew = context.tableApi.page.info(); + newPageIndex = Math.max(0, pageInfoNew.pages - 1); + Util.showNotify({title: 'More logs loaded', text: newLogCount + ' older logs added', type: 'info'}); + } + + // get to last page (pageIndex starts at zero) -> check if last page > 0 + context.tableApi.page(newPageIndex).draw(false); + }else{ + Util.showNotify({title: 'No logs found', text: 'No more entries', type: 'danger'}); + } + + }; + + // init logs table -------------------------------------------------------------------------------------------- + let logTable = $('
', { + class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed'].join(' ') + }); + logsElement.append(logTable); + + let serverDate = Util.getServerTime(); + let serverHours = serverDate.setHours(0,0,0,0); + + let logDataTable = logTable.DataTable({ + pageLength: 25, + paging: true, + lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]], + pagingType: 'full_numbers', + ordering: false, + autoWidth: false, + searching: true, + hover: false, + data: [], + language: { + emptyTable: 'No logs available', + zeroRecords: 'No logs found', + lengthMenu: 'Show _MENU_ rows', + info: 'Showing _START_ to _END_ of _TOTAL_ rows' + }, + columnDefs: [ + { + targets: 0, + title: ' ', + width: 12, + data: 'context.tag', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + let className = 'txt-color-' + data; + value = ''; + } + return value; + } + } + },{ + targets: 1, + name: 'timestamp', + title: '', + width: 100, + className: ['text-right'].join(' '), + data: 'datetime.date', + render: { + _: function(data, type, row, meta){ + // strip microseconds + let logDateString = data.substring(0, 19) ; + let logDate = new Date(logDateString.replace(/-/g, '/')); + data = Util.convertDateToString(logDate, true); + + // check whether log is new (today) -> + if(logDate.setHours(0,0,0,0) === serverHours) { + // replace dd/mm/YYYY + data = 'today' + data.substring(10); + } + return data; + } + } + },{ + targets: 2, + title: 'level', + width: 40, + data: 'level_name' + },{ + targets: 3, + title: 'channel', + className: [config.tableCellEllipsisClass].join(' '), + width: 40, + data: 'channel' + },{ + targets: 4, + title: 'message', + width: 115, + data: 'message', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + let className = 'txt-color-'; + if(Util.getObjVal(row, 'context.tag')){ + className += row.context.tag; + } + value = '' + value + ''; + } + return value; + } + } + },{ + targets: 5, + title: '', + width: 26, + searchable: false, + className: [config.tableCellImageClass].join(' '), + data: 'context.data.character.id' , + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = ''; + } + return value; + } + } + },{ + targets: 6, + title: 'pilot', + width: 110, + className: [config.tableCellActionClass].join(' '), + data: 'context.data.character.name', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value += ' ' + getIconForInformationWindow(); + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // open character information window (ingame) + $(cell).on('click', { tableApi: this.DataTable() }, function(e) { + let rowData = e.data.tableApi.row(this).data(); + Util.openIngameWindow(rowData.context.data.character.id); + }); + } + },{ + targets: 7, + title: 'context', + className: [config.tableCellEllipsisClass].join(' '), + data: 'context.data.formatted' + },{ + targets: 8, + title: '', + width: 12, + className: [config.tableCellActionClass].join(' '), + data: 'context.data', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + // txt-color-redDarker + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // unset formatted string (to much content) + + if(cellData.formatted){ + // clone data before delete() values + cellData = Object.assign({}, cellData); + delete(cellData.formatted); + } + + let jsonHighlighted = Render.highlightJson(cellData); + let content = '
' + jsonHighlighted + '
'; + + // open popover with raw log data + $(cell).popover({ + placement: 'left', + html: true, + trigger: 'hover', + content: content, + container: 'body', + title: 'Raw data', + delay: { + show: 180, + hide: 0 + } + }); + } + } + ], + initComplete: function(settings){ + let tableApi = this.api(); + + // empty table is ready -> load logs + getLogsData({ + mapId: mapData.config.id + }, { + tableApi: tableApi, + callback: updateTableDataCallback, + logsElement: logsElement + }); + }, + drawCallback: function(settings){ + let tableApi = this.api(); + + // en/disable "load more" button ---------------------------------------------------------------------- + let tableInfo = tableApi.page.info(); + let isLastPage = (tableInfo.pages === 0 || tableInfo.page === tableInfo.pages - 1); + tableApi.button(0).enable(isLastPage); + + // adjust "timestamp" column width -------------------------------------------------------------------- + let timestampColumn = tableApi.column('timestamp:name').header(); + let timestampColumnCells = tableApi.cells(undefined, 'timestamp:name', {page: 'current', order:'current'}); + + let hasOldLogs = timestampColumnCells.render( 'display' ).reduce((hasOldLogs, cellValue) => { + return (hasOldLogs === false && !cellValue.startsWith('today')) ? true : hasOldLogs; + }, false); + + if(hasOldLogs){ + $(timestampColumn).css({width: '100px'}); + }else{ + $(timestampColumn).css({width: '80px'}); + } + } + }); + + // ------------------------------------------------------------------------------------------------------------ + // add dataTable buttons (extension) + logsElement.append($('
', { + class: config.tableToolsClass + })); + + let buttons = new $.fn.dataTable.Buttons( logDataTable, { + buttons: [ + { + className: 'btn btn-sm btn-default', + text: ' load more', + enabled: false, + action: function ( e, dt, node, config ) { + let pageInfo = dt.page.info(); + + getLogsData({ + mapId: mapData.config.id, + limit: pageInfo.length, + offset: pageInfo.recordsTotal + }, { + tableApi: dt, + callback: updateTableDataCallback, + logsElement: logsElement + }); + } + } + ] + } ); + + logDataTable.buttons().container().appendTo( $(this).find('.' + config.tableToolsClass)); + }; + /** * shows the map information modal dialog * @param options @@ -830,21 +1173,29 @@ define([ let mapData = activeMap.getMapDataFromClient({forceData: true}); if(mapData !== false){ - requirejs(['text!templates/dialog/map_info.html', 'mustache'], function(template, Mustache) { + // "log" tab -> get "Origin", not all config options are set in mapData + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + + requirejs(['text!templates/dialog/map_info.html', 'mustache', 'datatables.loader'], (template, Mustache) => { let data = { dialogSummaryContainerId: config.dialogMapInfoSummaryId, dialogUsersContainerId: config.dialogMapInfoUsersId, + dialogLogsContainerId: config.dialogMapInfoLogsId, dialogRefreshContainerId: config.dialogMapInfoRefreshId, dialogNavigationClass: config.dialogNavigationClass, mapInfoId: config.mapInfoId, mapInfoSystemsId: config.mapInfoSystemsId, mapInfoConnectionsId: config.mapInfoConnectionsId, mapInfoUsersId: config.mapInfoUsersId, + mapInfoLogsId: config.mapInfoLogsId, + + logHistoryEnabled: Boolean(Util.getObjVal(mapDataOrigin, 'config.logging.history')), // default open tab ---------- openTabInformation: options.tab === 'information', - openTabActivity: options.tab === 'activity' + openTabActivity: options.tab === 'activity', + openTabLog: options.tab === 'log' }; let content = Mustache.render(template, data); @@ -865,39 +1216,51 @@ define([ }); mapInfoDialog.on('shown.bs.modal', function(e) { - // modal on open - let mapElement = $('#' + config.mapInfoId); let systemsElement = $('#' + config.mapInfoSystemsId); let connectionsElement = $('#' + config.mapInfoConnectionsId); let usersElement = $('#' + config.mapInfoUsersId); - // set refresh button observer - $('#' + config.dialogMapInfoRefreshId).on('click', function(){ + $('#' + config.dialogMapInfoRefreshId).on('click', function(e){ let menuAction = $(this).attr('data-action'); if(menuAction === 'refresh'){ // get new map data let mapData = activeMap.getMapDataFromClient({forceData: true}); + // find active tab + let activeTabLink = $(this).parents('.navbar').find('.navbar-header.pull-left li.active a'); + if(activeTabLink.attr('href') === '#' + config.dialogMapInfoLogsId){ + $('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin); + } - mapElement.loadMapInfoData(mapData); - systemsElement.loadSystemInfoTable(mapData); - connectionsElement.loadConnectionInfoTable(mapData); - usersElement.loadUsersInfoTable(mapData); + mapElement.initMapInfoData(mapData); + systemsElement.initSystemInfoTable(mapData); + connectionsElement.initConnectionInfoTable(mapData); + usersElement.initUsersInfoTable(mapData); } }); // load map data - mapElement.loadMapInfoData(mapData); + mapElement.initMapInfoData(mapData); // load system table - systemsElement.loadSystemInfoTable(mapData); + systemsElement.initSystemInfoTable(mapData); // load connection table - connectionsElement.loadConnectionInfoTable(mapData); + connectionsElement.initConnectionInfoTable(mapData); // load users table - usersElement.loadUsersInfoTable(mapData); + usersElement.initUsersInfoTable(mapData); + + }); + + // events for tab change + mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){ + if($(e.target).attr('href') === '#' + config.dialogMapInfoLogsId){ + // "log" tab + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + $('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin); + } }); }); diff --git a/js/app/ui/dialog/map_settings.js b/js/app/ui/dialog/map_settings.js index ecba3f2f..6a4a2dff 100644 --- a/js/app/ui/dialog/map_settings.js +++ b/js/app/ui/dialog/map_settings.js @@ -24,6 +24,15 @@ define([ deleteEolConnectionsId: 'pf-map-dialog-delete-connections-eol', // id for "deleteEOLConnections" checkbox persistentAliasesId: 'pf-map-dialog-persistent-aliases', // id for "persistentAliases" checkbox + logHistoryId: 'pf-map-dialog-history', // id for "history logging" checkbox + logActivityId: 'pf-map-dialog-activity', // id for "activity" checkbox + + slackWebHookURLId: 'pf-map-dialog-slack-url', // id for Slack "webHookUrl" + slackUsernameId: 'pf-map-dialog-slack-username', // id for Slack "username" + slackIconId: 'pf-map-dialog-slack-icon', // id for Slack "icon" + slackChannelHistoryId: 'pf-map-dialog-slack-channel-history', // id for Slack channel "history" + slackChannelRallyId: 'pf-map-dialog-slack-channel-rally', // id for Slack channel "rally" + characterSelectId: 'pf-map-dialog-character-select', // id for "character" select corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select @@ -67,7 +76,7 @@ define([ requirejs([ 'text!templates/dialog/map.html', - 'text!templates/form/map_settings.html', + 'text!templates/form/map.html', 'mustache' ], function(templateMapDialog, templateMapSettings, Mustache) { @@ -96,10 +105,10 @@ define([ formInfoContainerClass: Util.config.formInfoContainerClass }; - // render "new map" tab content ------------------------------------------- + // render "new map" tab content ----------------------------------------------------------------------- let contentNewMap = Mustache.render(templateMapSettings, data); - // render "edit map" tab content ------------------------------------------ + // render "edit map" tab content ---------------------------------------------------------------------- let contentEditMap = Mustache.render(templateMapSettings, data); contentEditMap = $(contentEditMap); @@ -111,6 +120,19 @@ define([ let deleteEolConnections = true; let persistentAliases = true; + let logActivity = true; + let logHistory = true; + + let slackWebHookURL = ''; + let slackUsername = ''; + let slackIcon = ''; + let slackChannelHistory = ''; + let slackChannelRally = ''; + let slackEnabled = false; + let slackHistoryEnabled = false; + let slackRallyEnabled = false; + let slackSectionShow = false; + if(mapData !== false){ // set current map information contentEditMap.find('input[name="id"]').val( mapData.config.id ); @@ -126,9 +148,26 @@ define([ deleteExpiredConnections = mapData.config.deleteExpiredConnections; deleteEolConnections = mapData.config.deleteEolConnections; persistentAliases = mapData.config.persistentAliases; + + logActivity = mapData.config.logging.activity; + logHistory = mapData.config.logging.history; + + slackWebHookURL = mapData.config.logging.slackWebHookURL; + slackUsername = mapData.config.logging.slackUsername; + slackIcon = mapData.config.logging.slackIcon; + slackChannelHistory = mapData.config.logging.slackChannelHistory; + slackChannelRally = mapData.config.logging.slackChannelRally; + slackEnabled = Boolean(Util.getObjVal(Init, 'slack.status')); + slackHistoryEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled')); + slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled')); + slackSectionShow = (slackEnabled && slackWebHookURL.length > 0); + + // remove "#" from Slack channels + slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory; + slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally; } - // render main dialog ----------------------------------------------------- + // render main dialog --------------------------------------------------------------------------------- data = { id: config.newMapDialogId, mapData: mapData, @@ -162,6 +201,26 @@ define([ deleteEolConnections: deleteEolConnections, persistentAliases: persistentAliases, + logHistoryId: config.logHistoryId, + logActivityId: config.logActivityId, + logActivity: logActivity, + logHistory: logHistory, + + slackWebHookURLId: config.slackWebHookURLId, + slackUsernameId: config.slackUsernameId, + slackIconId: config.slackIconId, + slackChannelHistoryId: config.slackChannelHistoryId, + slackChannelRallyId: config.slackChannelRallyId, + slackWebHookURL: slackWebHookURL, + slackUsername: slackUsername, + slackIcon: slackIcon, + slackChannelHistory: slackChannelHistory, + slackChannelRally: slackChannelRally, + slackEnabled: slackEnabled, + slackHistoryEnabled: slackHistoryEnabled, + slackRallyEnabled: slackRallyEnabled, + slackSectionShow: slackSectionShow, + characterSelectId: config.characterSelectId, corporationSelectId: config.corporationSelectId, allianceSelectId: config.allianceSelectId, @@ -244,6 +303,15 @@ define([ // get form data let formData = form.getFormValues(); + // add value prefixes (Slack channels) + let tmpVal; + if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelHistory')) === 'string' && tmpVal.length){ + formData.slackChannelHistory = '#' + tmpVal; + } + if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelRally')) === 'string' && tmpVal.length){ + formData.slackChannelRally = '#' + tmpVal; + } + // checkbox fix -> settings tab if( form.find('#' + config.deleteExpiredConnectionsId).length ){ formData.deleteExpiredConnections = formData.hasOwnProperty('deleteExpiredConnections') ? parseInt( formData.deleteExpiredConnections ) : 0; @@ -254,6 +322,15 @@ define([ if( form.find('#' + config.persistentAliasesId).length ){ formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0; } + if( form.find('#' + config.persistentAliasesId).length ){ + formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0; + } + if( form.find('#' + config.logHistoryId).length ){ + formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0; + } + if( form.find('#' + config.logActivityId).length ){ + formData.logActivity = formData.hasOwnProperty('logActivity') ? parseInt( formData.logActivity ) : 0; + } let requestData = {formData: formData}; @@ -264,8 +341,6 @@ define([ dataType: 'json' }).done(function(responseData){ - dialogContent.hideLoadingAnimation(); - if(responseData.error.length){ form.showFormMessage(responseData.error); }else{ @@ -287,6 +362,8 @@ define([ Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); + }).always(function() { + dialogContent.hideLoadingAnimation(); }); } @@ -297,10 +374,13 @@ define([ }); - // after modal is shown ======================================================================= + // after modal is shown =============================================================================== mapInfoDialog.on('shown.bs.modal', function(e){ mapInfoDialog.initTooltips(); + // manually trigger the "show" event for the initial active tab (not triggered by default...) + mapInfoDialog.find('.navbar li.active a[data-toggle=tab]').trigger('shown.bs.tab'); + // prevent "disabled" tabs from being clicked... "bootstrap" bugFix... mapInfoDialog.find('.navbar a[data-toggle=tab]').on('click', function(e){ if ($(this).hasClass('disabled')){ @@ -323,17 +403,12 @@ define([ form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]); } - // init select fields in case "settings" tab is open by default - if(options.tab === 'settings'){ - initSettingsSelectFields(mapInfoDialog); - } - - // init "download tab" ======================================================================== + // init "download tab" ============================================================================ let downloadTabElement = mapInfoDialog.find('#' + config.dialogMapDownloadContainerId); if(downloadTabElement.length){ // tab exists - // export map data ------------------------------------------------------------------------ + // export map data ---------------------------------------------------------------------------- downloadTabElement.find('#' + config.buttonExportId).on('click', { mapData: mapData }, function(e){ let exportForm = $('#' + config.dialogMapExportFormId); @@ -364,7 +439,7 @@ define([ } }); - // import map data ------------------------------------------------------------------------ + // import map data ---------------------------------------------------------------------------- // check if "FileReader" API is supported let importFormElement = downloadTabElement.find('#' + config.dialogMapImportFormId); if(window.File && window.FileReader && window.FileList && window.Blob){ @@ -477,16 +552,22 @@ define([ } }); - // events for tab change + // events for tab change ------------------------------------------------------------------------------ mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){ + let modalDialog = mapInfoDialog.find('div.modal-dialog'); let selectElementCharacter = mapInfoDialog.find('#' + config.characterSelectId); let selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId); let selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId); + if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){ - // "settings" tab + // "settings" tab -> resize modal + modalDialog.toggleClass('modal-lg', true); initSettingsSelectFields(mapInfoDialog); }else{ + // resize modal + modalDialog.toggleClass('modal-lg', false); + if( $(selectElementCharacter).data('select2') !== undefined ){ $(selectElementCharacter).select2('destroy'); } @@ -611,29 +692,38 @@ define([ * @param mapData */ $.fn.showDeleteMapDialog = function(mapData){ - let mapName = mapData.config.name; + let mapNameStr = '' + mapName + ''; - let mapDeleteDialog = bootbox.confirm('Delete map "' + mapName + '"?', function(result){ - if(result){ - let data = {mapData: mapData.config}; + let mapDeleteDialog = bootbox.confirm({ + message: 'Delete map "' + mapNameStr + '"?', + buttons: { + confirm: { + label: ' delete map', + className: 'btn-danger' + } + }, + callback: function(result){ + if(result){ + let data = {mapData: mapData.config}; - $.ajax({ - type: 'POST', - url: Init.path.deleteMap, - data: data, - dataType: 'json' - }).done(function(data){ - Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'}); - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function() { - $(mapDeleteDialog).modal('hide'); - }); + $.ajax({ + type: 'POST', + url: Init.path.deleteMap, + data: data, + dataType: 'json' + }).done(function(data){ + Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'}); + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'}); + $(document).setProgramStatus('problem'); + }).always(function() { + $(mapDeleteDialog).modal('hide'); + }); - return false; + return false; + } } }); diff --git a/js/app/ui/dialog/stats.js b/js/app/ui/dialog/stats.js index 0d797751..0b814f9d 100644 --- a/js/app/ui/dialog/stats.js +++ b/js/app/ui/dialog/stats.js @@ -8,7 +8,8 @@ define([ 'app/init', 'app/util', 'app/render', - 'bootbox' + 'bootbox', + 'peityInlineChart' ], function($, Init, Util, Render, bootbox) { 'use strict'; @@ -25,7 +26,7 @@ define([ // stats/dataTable statsContainerId: 'pf-stats-dialog-container', // class for statistics container (dynamic ajax content) statsTableId: 'pf-stats-table', // id for statistics table element - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells // charts statsLineChartClass: 'pf-line-chart' // class for inline chart elements @@ -36,7 +37,9 @@ define([ * @param dialogElement */ let initStatsTable = function(dialogElement){ - let columnNumberWidth = 35; + let columnNumberWidth = 28; + let cellPadding = 4; + let lineChartWidth = columnNumberWidth + (2 * cellPadding); let lineColor = '#477372'; // render function for inline-chart columns @@ -71,7 +74,7 @@ define([ lengthMenu: [[10, 20, 30, 50], [10, 20, 30, 50]], paging: true, ordering: true, - order: [ 16, 'desc' ], + order: [ 20, 'desc' ], info: true, searching: true, hover: false, @@ -97,11 +100,11 @@ define([ orderable: false, searchable: false, width: 26, - className: ['text-center', config.tableImageCellClass].join(' '), + className: ['text-center', config.tableCellImageClass].join(' '), data: 'character', render: { _: function(data, type, row, meta){ - return ''; + return ''; } } },{ @@ -134,7 +137,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemCreate', + data: 'mapCreate', render: { _: renderInlineChartColumn } @@ -145,7 +148,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemUpdate', + data: 'mapUpdate', render: { _: renderInlineChartColumn } @@ -156,7 +159,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemDelete', + data: 'mapDelete', render: { _: renderInlineChartColumn } @@ -166,7 +169,7 @@ define([ searchable: false, width: 20, className: ['text-right', 'separator-right'].join(' ') , - data: 'systemSum', + data: 'mapSum', render: { _: renderNumericColumn } @@ -177,7 +180,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionCreate', + data: 'systemCreate', render: { _: renderInlineChartColumn } @@ -188,7 +191,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionUpdate', + data: 'systemUpdate', render: { _: renderInlineChartColumn } @@ -199,7 +202,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionDelete', + data: 'systemDelete', render: { _: renderInlineChartColumn } @@ -208,8 +211,8 @@ define([ title: 'Σ  ', searchable: false, width: 20, - className: ['text-right', 'separator-right'].join(' '), - data: 'connectionSum', + className: ['text-right', 'separator-right'].join(' ') , + data: 'systemSum', render: { _: renderNumericColumn } @@ -220,7 +223,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureCreate', + data: 'connectionCreate', render: { _: renderInlineChartColumn } @@ -231,7 +234,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureUpdate', + data: 'connectionUpdate', render: { _: renderInlineChartColumn } @@ -242,7 +245,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureDelete', + data: 'connectionDelete', render: { _: renderInlineChartColumn } @@ -252,12 +255,55 @@ define([ searchable: false, width: 20, className: ['text-right', 'separator-right'].join(' '), - data: 'signatureSum', + data: 'connectionSum', render: { _: renderNumericColumn } },{ targets: 16, + title: 'C  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureCreate', + render: { + _: renderInlineChartColumn + } + },{ + targets: 17, + title: 'U  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureUpdate', + render: { + _: renderInlineChartColumn + } + },{ + targets: 18, + title: 'D  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureDelete', + render: { + _: renderInlineChartColumn + } + },{ + targets: 19, + title: 'Σ  ', + searchable: false, + width: 20, + className: ['text-right', 'separator-right'].join(' '), + data: 'signatureSum', + render: { + _: renderNumericColumn + } + },{ + targets: 20, title: 'Σ  ', searchable: false, width: 20, @@ -277,17 +323,17 @@ define([ }, drawCallback: function(settings){ this.api().rows().nodes().to$().each(function(i, row){ - $(row).find('.' + config.statsLineChartClass).peity('line', { + $($(row).find('.' + config.statsLineChartClass)).peity('line', { fill: 'transparent', height: 18, min: 0, - width: 50 + width: lineChartWidth }); }); }, footerCallback: function ( row, data, start, end, display ) { let api = this.api(); - let sumColumnIndexes = [7, 11, 15, 16]; + let sumColumnIndexes = [7, 11, 15, 19, 20]; // column data for "sum" columns over this page let pageTotalColumns = api @@ -310,7 +356,16 @@ define([ statsTable.on('order.dt search.dt', function(){ statsTable.column(0, {search:'applied', order:'applied'}).nodes().each(function(cell, i){ - $(cell).html( (i + 1) + '.  '); + let rowCount = i + 1; + let content = ''; + switch(rowCount){ + case 1: content = ''; break; + case 2: content = ''; break; + case 3: content = ''; break; + default: content = rowCount + '.  '; + } + + $(cell).html(content); }); }).draw(); @@ -413,6 +468,9 @@ define([ let currentWeek = weekStart; let formattedWeeksData = { + mapCreate: [], + mapUpdate: [], + mapDelete: [], systemCreate: [], systemUpdate: [], systemDelete: [], @@ -422,6 +480,7 @@ define([ signatureCreate: [], signatureUpdate: [], signatureDelete: [], + mapSum: 0, systemSum: 0, connectionSum: 0, signatureSum: 0 @@ -433,6 +492,16 @@ define([ if(weeksData.hasOwnProperty( yearWeekProp )){ let weekData = weeksData[ yearWeekProp ]; + // map ---------------------------------------------------------------------------------- + formattedWeeksData.mapCreate.push( weekData.mapCreate ); + formattedWeeksData.mapSum += parseInt( weekData.mapCreate ); + + formattedWeeksData.mapUpdate.push( weekData.mapUpdate ); + formattedWeeksData.mapSum += parseInt( weekData.mapUpdate ); + + formattedWeeksData.mapDelete.push( weekData.mapDelete ); + formattedWeeksData.mapSum += parseInt( weekData.mapDelete ); + // system ------------------------------------------------------------------------------- formattedWeeksData.systemCreate.push( weekData.systemCreate ); formattedWeeksData.systemSum += parseInt( weekData.systemCreate ); @@ -463,6 +532,11 @@ define([ formattedWeeksData.signatureDelete.push( weekData.signatureDelete ); formattedWeeksData.signatureSum += parseInt( weekData.signatureDelete ); }else{ + // map ------------------------------------------------------------------------------- + formattedWeeksData.mapCreate.push(0); + formattedWeeksData.mapUpdate.push(0); + formattedWeeksData.mapDelete.push(0); + // system ------------------------------------------------------------------------------- formattedWeeksData.systemCreate.push(0); formattedWeeksData.systemUpdate.push(0); @@ -487,6 +561,11 @@ define([ } } + // map --------------------------------------------------------------------------------------- + formattedWeeksData.mapCreate = formattedWeeksData.mapCreate.join(','); + formattedWeeksData.mapUpdate = formattedWeeksData.mapUpdate.join(','); + formattedWeeksData.mapDelete = formattedWeeksData.mapDelete.join(','); + // system --------------------------------------------------------------------------------------- formattedWeeksData.systemCreate = formattedWeeksData.systemCreate.join(','); formattedWeeksData.systemUpdate = formattedWeeksData.systemUpdate.join(','); @@ -515,6 +594,19 @@ define([ name: data.name, lastLogin: data.lastLogin }, + mapCreate: { + type: 'C', + data: formattedWeeksData.mapCreate + }, + mapUpdate: { + type: 'U', + data: formattedWeeksData.mapUpdate + }, + mapDelete: { + type: 'D', + data: formattedWeeksData.mapDelete + }, + mapSum: formattedWeeksData.mapSum, systemCreate: { type: 'C', data: formattedWeeksData.systemCreate @@ -554,7 +646,8 @@ define([ data: formattedWeeksData.signatureDelete }, signatureSum: formattedWeeksData.signatureSum, - totalSum: formattedWeeksData.systemSum + formattedWeeksData.connectionSum + formattedWeeksData.signatureSum + totalSum: formattedWeeksData.mapSum + formattedWeeksData.systemSum + + formattedWeeksData.connectionSum + formattedWeeksData.signatureSum }; formattedData.push(rowData); @@ -602,18 +695,18 @@ define([ * @param type * @returns {boolean} */ - let isTabTypeEnabled = function(type){ + let isTabTypeEnabled = (type) => { let enabled = false; switch(type){ case 'private': - if(Init.mapTypes.private.defaultConfig.activity_logging){ + if( Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) ){ enabled = true; } break; case 'corporation': if( - Init.mapTypes.corporation.defaultConfig.activity_logging && + Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) && Util.getCurrentUserInfo('corporationId') ){ enabled = true; @@ -621,7 +714,7 @@ define([ break; case 'alliance': if( - Init.mapTypes.alliance.defaultConfig.activity_logging && + Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) && Util.getCurrentUserInfo('allianceId') ){ enabled = true; @@ -636,15 +729,46 @@ define([ * show activity stats dialog */ $.fn.showStatsDialog = function(){ - requirejs(['text!templates/dialog/stats.html', 'mustache', 'peityInlineChart'], function(template, Mustache) { + requirejs(['text!templates/dialog/stats.html', 'mustache', 'datatables.loader'], function(template, Mustache) { + // get current statistics map settings + let logActivityEnabled = false; + let activeMap = Util.getMapModule().getActiveMap(); + if(activeMap){ + let activeMapId = activeMap.data('id'); + let activeMapData = Util.getCurrentMapData(activeMapId); + if(activeMapData){ + logActivityEnabled = Boolean(Util.getObjVal(activeMapData, 'config.logging.activity')); + } + } + + // check which dialog tab is default active + let enablePrivateTab = isTabTypeEnabled('private'); + let enableCorporationTab = isTabTypeEnabled('corporation'); + let enableAllianceTab = isTabTypeEnabled('alliance'); + + let activePrivateTab = false; + let activeCorporationTab = false; + let activeAllianceTab = false; + + if(enableCorporationTab){ + activeCorporationTab = true; + }else if(enableAllianceTab){ + activeAllianceTab = true; + }else if(enablePrivateTab){ + activePrivateTab = true; + } let data = { id: config.statsDialogId, dialogNavigationClass: config.dialogNavigationClass, dialogNavLiClass: config.dialogNavigationListItemClass, - enablePrivateTab: isTabTypeEnabled('private'), - enableCorporationTab: isTabTypeEnabled('corporation'), - enableAllianceTab: isTabTypeEnabled('alliance'), + enablePrivateTab: enablePrivateTab, + enableCorporationTab: enableCorporationTab, + enableAllianceTab: enableAllianceTab, + activePrivateTab: activePrivateTab, + activeCorporationTab: activeCorporationTab, + activeAllianceTab: activeAllianceTab, + logActivityEnabled: logActivityEnabled, statsContainerId: config.statsContainerId, statsTableId: config.statsTableId, dialogNavigationOffsetClass: config.dialogNavigationOffsetClass, @@ -670,7 +794,6 @@ define([ // model events statsDialog.on('show.bs.modal', function(e) { let dialogElement = $(e.target); - initStatsTable(dialogElement); }); diff --git a/js/app/ui/form_element.js b/js/app/ui/form_element.js index 649fa592..94d14e6b 100644 --- a/js/app/ui/form_element.js +++ b/js/app/ui/form_element.js @@ -197,15 +197,15 @@ define([ switch(options.type){ case 'character': - imagePath = Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg'; + imagePath = Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg'; previewContent = ''; break; case 'corporation': - imagePath = Init.url.ccpImageServer + 'Corporation/' + data.id + '_32.png'; + imagePath = Init.url.ccpImageServer + '/Corporation/' + data.id + '_32.png'; previewContent = ''; break; case 'alliance': - imagePath = Init.url.ccpImageServer + 'Alliance/' + data.id + '_32.png'; + imagePath = Init.url.ccpImageServer + '/Alliance/' + data.id + '_32.png'; previewContent = ''; break; } diff --git a/js/app/ui/system_graph.js b/js/app/ui/system_graph.js index 4dba2c02..965f8dcb 100644 --- a/js/app/ui/system_graph.js +++ b/js/app/ui/system_graph.js @@ -10,7 +10,7 @@ define([ ], function($, Init, Util, Morris) { 'use strict'; - var config = { + let config = { // module info moduleClass: 'pf-module', // class for each module @@ -53,8 +53,8 @@ define([ * @param option * @returns {string} */ - var getInfoForGraph = function(graphKey, option){ - var info = ''; + let getInfoForGraph = function(graphKey, option){ + let info = ''; if(config.systemGraphLabels.hasOwnProperty(graphKey)){ info = config.systemGraphLabels[graphKey][option]; @@ -69,14 +69,14 @@ define([ * @param graphKey * @param graphData */ - var initGraph = function(graphElement, graphKey, graphData, eventLine){ + let initGraph = function(graphElement, graphKey, graphData, eventLine){ if(graphData.length > 0){ - var labelYFormat = function(y){ + let labelYFormat = function(y){ return Math.round(y); }; - var graphConfig = { + let graphConfig = { element: graphElement, data: graphData, xkey: 'x', @@ -121,24 +121,24 @@ define([ * @param parentElement * @param systemData */ - var drawModule = function(parentElement, systemData){ + let drawModule = function(parentElement, systemData){ // graph data is available for k-space systems if(systemData.type.id === 2){ - var requestData = { + let requestData = { systemIds: [systemData.systemId] }; // calculate time offset until system created - var serverData = Util.getServerTime(); + let serverData = Util.getServerTime(); - var timestampNow = Math.floor(serverData.getTime() / 1000); - var timeSinceUpdate = timestampNow - systemData.updated; + let timestampNow = Math.floor(serverData.getTime() / 1000); + let timeSinceUpdate = timestampNow - systemData.updated; - var timeInHours = Math.floor(timeSinceUpdate / 3600); - var timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60); - var timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2); - var eventLine = timeInHours + timeInMinutesPercent; + 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; @@ -152,7 +152,7 @@ define([ if( Object.keys(systemGraphsData).length > 0 ){ // create new (hidden) module container - var moduleElement = $('
', { + let moduleElement = $('
', { class: [config.moduleClass, config.systemGraphModuleClass].join(' '), css: {opacity: 0} }); @@ -165,7 +165,7 @@ define([ } // row element - var rowElement = $('
', { + let rowElement = $('
', { class: 'row' }); moduleElement.append(rowElement); @@ -173,15 +173,15 @@ define([ $.each(systemGraphsData, function(systemId, graphsData){ $.each(graphsData, function(graphKey, graphData){ - var colElement = $('
', { + let colElement = $('
', { class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ') }); - var headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); + let headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); colElement.append(headlineElement); - var graphElement = $('
', { + let graphElement = $('
', { class: config.systemGraphClass }); @@ -203,7 +203,7 @@ define([ }); } }).fail(function( jqXHR, status, error) { - var reason = status + ' ' + error; + let reason = status + ' ' + error; Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); }); @@ -218,10 +218,10 @@ define([ */ $.fn.drawSystemGraphModule = function(systemData){ - var parentElement = $(this); + let parentElement = $(this); // check if module already exists - var moduleElement = parentElement.find('.' + config.systemGraphModuleClass); + let moduleElement = parentElement.find('.' + config.systemGraphModuleClass); if(moduleElement.length > 0){ moduleElement.velocity('transition.slideDownOut', { diff --git a/js/app/ui/system_killboard.js b/js/app/ui/system_killboard.js index 32913f02..b58483b0 100644 --- a/js/app/ui/system_killboard.js +++ b/js/app/ui/system_killboard.js @@ -30,9 +30,10 @@ define([ }; /** - * get label element with given content + * * @param text - * @returns {*|XMLList} + * @param options + * @returns {jQuery} */ let getLabel = function(text, options){ let label = $('', { @@ -42,7 +43,6 @@ define([ return label; }; - let showKillmails = function(moduleElement, killboardData){ // show number of killMails @@ -61,7 +61,7 @@ define([ break; } - moduleElement.append( $('
').text(i + 'h ago')); + moduleElement.append( $('
').text( i ? i + 'h ago' : 'recent')); let killMailData = killboardData.tableData[i].killmails; @@ -77,25 +77,25 @@ define([ let killData = killMailData[j]; - let linkUrl = '//zkillboard.com/kill/' + killData.killID + '/'; - let victimImageUrl = Init.url.ccpImageServer + 'Type/' + killData.victim.shipTypeID + '_64.png'; - let killDate = getDateObjectByTimeString(killData.killTime); + let linkUrl = '//zkillboard.com/kill/' + killData.killmail_id + '/'; + let victimImageUrl = Init.url.ccpImageServer + '/Type/' + killData.victim.ship_type_id + '_64.png'; + let killDate = Util.convertDateToUTC(new Date(killData.killmail_time)); let killDateString = Util.convertDateToString(killDate); let killLossValue = Util.formatPrice( killData.zkb.totalValue ); // check for ally let victimAllyLogoUrl = ''; let displayAlly = 'none'; - if(killData.victim.allianceID > 0){ - victimAllyLogoUrl = Init.url.ccpImageServer + 'Alliance/' + killData.victim.allianceID + '_32.png'; + if(killData.victim.alliance_id > 0){ + victimAllyLogoUrl = Init.url.ccpImageServer + '/Alliance/' + killData.victim.alliance_id + '_32.png'; displayAlly = 'block'; } // check for corp let victimCorpLogoUrl = ''; let displayCorp = 'none'; - if(killData.victim.corporationID > 0){ - victimCorpLogoUrl = Init.url.ccpImageServer + 'Corporation/' + killData.victim.corporationID + '_32.png'; + if(killData.victim.corporation_id > 0){ + victimCorpLogoUrl = Init.url.ccpImageServer + '/Corporation/' + killData.victim.corporation_id + '_32.png'; displayCorp = 'inline'; } @@ -126,7 +126,7 @@ define([ text: killData.victim.characterName }).prepend( $('', { - text: killDateString + ' - ' + text: killDateString }) ).prepend( $('', { @@ -324,7 +324,7 @@ define([ wSpaceLinkModifier = 'w-space/'; } - let url = Init.url.zKillboard; + let url = Init.url.zKillboard + '/'; url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/'; killboardGraphElement.showLoadingAnimation(); @@ -338,16 +338,14 @@ define([ // the API wont return more than 200KMs ! - remember last bar block with complete KM information let lastCompleteDiffHourData = 0; - // loop kills and count kills by hour for (let i = 0; i < kbData.length; i++) { let killmailData = kbData[i]; - - let killDate = getDateObjectByTimeString(killmailData.killTime); + let killDate = Util.convertDateToUTC(new Date(killmailData.killmail_time)); // get time diff let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60); - let timeDiffHour = Math.round(timeDiffMin / 60); + let timeDiffHour = Math.floor(timeDiffMin / 60); // update chart data if (chartData[timeDiffHour]) { @@ -420,22 +418,11 @@ define([ }); }; - /** - * transform timestring - * @param timeString - * @returns {Date} - */ - let getDateObjectByTimeString = function(timeString){ - let match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/); - let date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]); - - return date; - }; - /** * get module element + * @param parentElement * @param systemData - * @returns {*|HTMLElement} + * @returns {*|jQuery|HTMLElement} */ let getModule = function(parentElement, systemData){ diff --git a/js/app/util.js b/js/app/util.js index 58f4045f..350df026 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -4,8 +4,8 @@ define([ 'jquery', 'app/init', - 'config/system_effect', - 'config/signature_type', + 'conf/system_effect', + 'conf/signature_type', 'bootbox', 'localForage', 'velocity', @@ -264,7 +264,8 @@ define([ errors[i].field.length > 0 ){ let formField = formElement.find('[name="' + errors[i].field + '"]'); - formField.parents('.form-group').removeClass('has-success').addClass('has-error'); + let formGroup = formField.parents('.form-group').removeClass('has-success').addClass('has-error'); + let formHelp = formGroup.find('.help-block').text(errors[i].message); } }else if(errors[i].type === 'warning'){ @@ -559,6 +560,7 @@ define([ let data = { id: config.headCharacterSwitchId, + browserTabId: getBrowserTabId(), routes: Init.routes, userData: userData, otherCharacters: $.grep( userData.characters, function( character ) { @@ -729,17 +731,24 @@ define([ let defaultOptions = { dismissible: true, + messageId: 'pf-alert-' + Math.random().toString(36).substring(7), messageTypeClass: messageTypeClass, - messageTextClass: messageTextClass + messageTextClass: messageTextClass, + insertElement: 'replace' }; defaultOptions = $.extend(defaultOptions, config); - let content = Mustache.render(template, defaultOptions); - containerElement.html(content); + switch(defaultOptions.insertElement){ + case 'replace': containerElement.html(content); break; + case 'prepend': containerElement.prepend(content); break; + case 'append': containerElement.append(content); break; + default: console.error('insertElement: %s is not specified!', defaultOptions.insertElement); + } - containerElement.children().first().velocity('stop').velocity('fadeIn'); + //containerElement.children().first().velocity('stop').velocity('fadeIn'); + $('#' + defaultOptions.messageId).velocity('stop').velocity('fadeIn'); }); }; @@ -1072,6 +1081,20 @@ define([ return Init.currentUserData; }; + /** + * get a unique ID for each tab + * -> store ID in session storage + */ + let getBrowserTabId = () => { + let key = 'tabId'; + let tabId = sessionStorage.getItem(key); + if(tabId === null){ + tabId = Math.random().toString(36).substr(2, 5); + sessionStorage.setItem(key, tabId); + } + return tabId; + }; + /** * set default jQuery AJAX configuration */ @@ -1081,6 +1104,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(); @@ -2058,34 +2084,59 @@ define([ }; /** - * Create Date as UTC + * clear session Storage + * -> otherwise a tab refresh does not clear sessionStorage! + */ + let clearSessionStorage = () => { + if(sessionStorage){ + sessionStorage.clear(); + } + }; + + /** + * Create Date() as UTC * @param date * @returns {Date} */ - let createDateAsUTC = function(date) { + let createDateAsUTC = function(date){ return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); }; /** - * Convert Date to UTC (!important function!) + * Convert Date() to UTC (!important function!) * @param date * @returns {Date} */ - let convertDateToUTC = function(date) { + let convertDateToUTC = function(date){ return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()); }; /** - * Convert Date to Time String + * Convert Date() to Time String * @param date + * @param showSeconds * @returns {string} */ - let convertDateToString = function(date){ + let convertDateToString = function(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) : ''; return dateString + ' ' + timeString; }; + /** + * get deep json object value if exists + * -> e.g. key = 'first.last.third' string + * @param obj + * @param key + * @returns {*} + */ + let getObjVal = (obj, key) => { + return key.split('.').reduce((o, x) => { + return (typeof o === 'undefined' || o === null) ? o : o[x]; + }, obj); + }; + /** * get document path * -> www.pathfinder.com/pathfinder/ -> /pathfinder @@ -2197,11 +2248,15 @@ define([ getNearBySystemData: getNearBySystemData, getNearByCharacterData: getNearByCharacterData, setDestination: setDestination, + convertDateToUTC: convertDateToUTC, convertDateToString: convertDateToString, getOpenDialogs: getOpenDialogs, openIngameWindow: openIngameWindow, formatPrice: formatPrice, getLocalStorage: getLocalStorage, + clearSessionStorage: clearSessionStorage, + getBrowserTabId: getBrowserTabId, + getObjVal: getObjVal, getDocumentPath: getDocumentPath, redirect: redirect, logout: logout diff --git a/js/lib/bootbox.min.js b/js/lib/bootbox.min.js index 0dc0cbd5..cb8edd0a 100644 --- a/js/lib/bootbox.min.js +++ b/js/lib/bootbox.min.js @@ -3,4 +3,4 @@ * * http://bootboxjs.com/license.txt */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file diff --git a/js/lib/jquery.dragToSelect.js b/js/lib/jquery.dragToSelect.js index e37756fb..98937ab4 100644 --- a/js/lib/jquery.dragToSelect.js +++ b/js/lib/jquery.dragToSelect.js @@ -93,13 +93,26 @@ jQuery.fn.dragToSelect = function (conf) { return this; } - var parentOffset = parent.offset(); - var parentDim = { - left: parentOffset.left, - top: parentOffset.top, - width: parent.width(), - height: parent.height() - }; + var parentDim = { + left: 0, + top: 0, + width: 10, + height: 10 + }; + + // set parent dimensions + // -> should be updated in case of left/right menu is open + var setParentDimensions = (parent) => { + var parentOffset = parent.offset(); + parentDim = { + left: parentOffset.left, + top: parentOffset.top, + width: parent.width(), + height: parent.height() + }; + } + + setParentDimensions(parent); // Current origin of select box var selectBoxOrigin = { @@ -343,6 +356,7 @@ jQuery.fn.dragToSelect = function (conf) { // Do the right stuff then return this -------------------------------------------------------- selectBox.mousemove(function(e){ + setParentDimensions(parent); lastMousePosition.x = e.pageX; lastMousePosition.y = e.pageY; e.preventDefault(); @@ -353,7 +367,6 @@ jQuery.fn.dragToSelect = function (conf) { e.which === 1 && // left mouse down 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()) { return; @@ -366,6 +379,7 @@ jQuery.fn.dragToSelect = function (conf) { e.preventDefault(); }).mousemove(function(e){ + setParentDimensions(parent); lastMousePosition.x = e.pageX; lastMousePosition.y = e.pageY; e.preventDefault(); diff --git a/js/lib/validator.min.js b/js/lib/validator.min.js index 9a49ff3d..71916ad1 100644 --- a/js/lib/validator.min.js +++ b/js/lib/validator.min.js @@ -1,9 +1,9 @@ /*! - * Validator v0.10.1 for Bootstrap 3, by @1000hz - * Copyright 2016 Cina Saffary + * Validator v0.11.9 for Bootstrap 3, by @1000hz + * Copyright 2017 Cina Saffary * Licensed under http://opensource.org/licenses/MIT * * https://github.com/1000hz/bootstrap-validator */ -+function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:a.trim(b.val())}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.$element=a(c),this.$inputs=this.$element.find(d.INPUT_SELECTOR),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),e.errors=a.extend({},d.DEFAULTS.errors,e.errors);for(var f in e.custom)if(!e.errors[f])throw new Error("Missing default error message for custom validator: "+f);a.extend(d.VALIDATORS,e.custom),this.$element.attr("novalidate",!0),this.toggleSubmit(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",d.INPUT_SELECTOR,a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.data("match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})})};d.INPUT_SELECTOR=':input:not([type="submit"], button):enabled:visible',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?b.checkValidity():!0},match:function(b){var c=b.data("match");return!b.val()||b.val()===a(c).val()},minlength:function(a){var b=a.data("minlength");return!a.val()||a.val().length>=b}},d.prototype.onInput=function(b){var c=this,d=a(b.target),e="focusout"!==b.type;this.validateInput(d,e).done(function(){c.toggleSubmit()})},d.prototype.validateInput=function(c,d){var e=b(c),f=c.data("bs.validator.previous"),g=c.data("bs.validator.errors");if(f===e)return a.Deferred().resolve();c.data("bs.validator.previous",e),c.is('[type="radio"]')&&(c=this.$element.find('input[name="'+c.attr("name")+'"]'));var h=a.Event("validate.bs.validator",{relatedTarget:c[0]});if(this.$element.trigger(h),!h.isDefaultPrevented()){var i=this;return this.runValidators(c).done(function(b){c.data("bs.validator.errors",b),b.length?d?i.defer(c,i.showErrors):i.showErrors(c):i.clearErrors(c),g&&b.toString()===g.toString()||(h=b.length?a.Event("invalid.bs.validator",{relatedTarget:c[0],detail:b}):a.Event("valid.bs.validator",{relatedTarget:c[0],detail:g}),i.$element.trigger(h)),i.toggleSubmit(),i.$element.trigger(a.Event("validated.bs.validator",{relatedTarget:c[0]}))})}},d.prototype.runValidators=function(c){function e(a){return c.data(a+"-error")||c.data("error")||"native"==a&&c[0].validationMessage||h.errors[a]}var f=[],g=a.Deferred(),h=this.options;return c.data("bs.validator.deferred")&&c.data("bs.validator.deferred").reject(),c.data("bs.validator.deferred",g),a.each(d.VALIDATORS,a.proxy(function(a,d){if((b(c)||c.attr("required"))&&(c.data(a)||"native"==a)&&!d.call(this,c)){var g=e(a);!~f.indexOf(g)&&f.push(g)}},this)),!f.length&&b(c)&&c.data("remote")?this.defer(c,function(){var d={};d[c.attr("name")]=b(c),a.get(c.data("remote"),d).fail(function(a,b,c){f.push(e("remote")||c)}).always(function(){g.resolve(f)})}):g.resolve(f),g.promise()},d.prototype.validate=function(){var b=this;return a.when(this.$inputs.map(function(){return b.validateInput(a(this),!1)})).then(function(){b.toggleSubmit(),b.$btn.hasClass("disabled")&&b.focusError()}),this},d.prototype.focusError=function(){if(this.options.focus){var b=a(".has-error:first :input");a(document.body).animate({scrollTop:b.offset().top-d.FOCUS_OFFSET},250),b.focus()}},d.prototype.showErrors=function(b){var c=this.options.html?"html":"text",d=b.data("bs.validator.errors"),e=b.closest(".form-group"),f=e.find(".help-block.with-errors"),g=e.find(".form-control-feedback");d.length&&(d=a("
    ").addClass("list-unstyled").append(a.map(d,function(b){return a("
  • ")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){return!b(a(this))}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),this.$btn.hasClass("disabled")&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.destroy=function(){return this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator").find(".form-control-feedback").removeClass([this.options.feedback.error,this.options.feedback.success].join(" ")),this.$inputs.off(".bs.validator").removeData(["bs.validator.errors","bs.validator.deferred","bs.validator.previous"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$element.find('input[type="submit"], button[type="submit"]').removeClass("disabled"),this.$element.find(".has-error, .has-danger").removeClass("has-error has-danger"),this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery); \ No newline at end of file ++function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:b.is("select[multiple]")?(b.val()||[]).length:b.val()}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.validators=a.extend({},d.VALIDATORS,e.custom),this.$element=a(c),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),this.update(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.on("reset.bs.validator",a.proxy(this.reset,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.attr("data-match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})}),this.$inputs.filter(function(){return b(a(this))&&!a(this).closest(".has-error").length}).trigger("focusout"),this.$element.attr("novalidate",!0)};d.VERSION="0.11.9",d.INPUT_SELECTOR=':input:not([type="hidden"], [type="submit"], [type="reset"], button)',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?!b.checkValidity()&&!b.validity.valid&&(b.validationMessage||"error!"):void 0},match:function(b){var c=b.attr("data-match");return b.val()!==a(c).val()&&d.DEFAULTS.errors.match},minlength:function(a){var b=a.attr("data-minlength");return a.val().length").addClass("list-unstyled").append(a.map(d,function(b){return a("
  • ")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger has-success"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&e.removeClass(this.options.feedback.success)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){var c=b(a(this));return!("string"==typeof c?a.trim(c):c)}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),(this.isIncomplete()||this.hasErrors())&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.reset=function(){return this.$element.find(".form-control-feedback").removeClass(this.options.feedback.error).removeClass(this.options.feedback.success),this.$inputs.removeData(["bs.validator.errors","bs.validator.deferred"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$btn.removeClass("disabled"),this.$element.find(".has-error, .has-danger, .has-success").removeClass("has-error has-danger has-success"),this},d.prototype.destroy=function(){return this.reset(),this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator"),this.$inputs.off(".bs.validator"),this.options=null,this.validators=null,this.$element=null,this.$btn=null,this.$inputs=null,this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery); \ No newline at end of file diff --git a/public/css/pathfinder.css b/public/css/pathfinder.css index 26623341..f1f13645 100644 --- a/public/css/pathfinder.css +++ b/public/css/pathfinder.css @@ -15,10 +15,10 @@ * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world by @mdo and @fat. - */*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:transparent}body{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;font-size:12px;line-height:1.5;color:#63676a;background-color:#1d1d1d}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#375959;text-decoration:none}a:hover,a:focus{color:#1a2a2a;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89;text-decoration:none}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.5;background-color:#1d1d1d;border:1px solid #ddd;border-radius:0px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:18px;margin-bottom:18px;border:0;border-top:1px solid #adadad}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:700;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:normal;line-height:1;color:#63676a}h1,.h1,h2,.h2,h3,.h3{margin-top:18px;margin-bottom:9px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:9px;margin-bottom:9px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:22px}h2,.h2{font-size:20px}h3,.h3{font-size:17px}h4,.h4{font-size:12px}h5,.h5{font-size:14px}h6,.h6{font-size:13px}p{margin:0 0 9px}.lead{margin-bottom:18px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width: 780px){.lead{font-size:16px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#63676a}.text-primary{color:#375959}a.text-primary:hover{color:#243939}.text-success{color:#2b2b2b}a.text-success:hover{color:#121212}.text-info{color:#2b2b2b}a.text-info:hover{color:#121212}.text-warning{color:#2b2b2b}a.text-warning:hover{color:#121212}.text-danger{color:#2b2b2b}a.text-danger:hover{color:#121212}.bg-primary{color:#fff}.bg-primary{background-color:#375959}a.bg-primary:hover{background-color:#243939}.bg-success{background-color:#d1e8d1}a.bg-success:hover{background-color:#afd7af}.bg-info{background-color:#abc9e2}a.bg-info:hover{background-color:#85b0d5}.bg-warning{background-color:#fdedd8}a.bg-warning:hover{background-color:#fad8a8}.bg-danger{background-color:#f6d1d0}a.bg-danger:hover{background-color:#eda7a5}.page-header{padding-bottom:8px;margin:36px 0 18px;border-bottom:1px solid #adadad}ul,ol{margin-top:0;margin-bottom:9px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled,.list-inline{padding-left:0;list-style:none}.list-inline{margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:18px}dt,dd{line-height:1.5}dt{font-weight:bold}dd{margin-left:0}@media (min-width: 480px){.dl-horizontal dt{float:left;width:80px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:100px}.dl-horizontal dd:before,.dl-horizontal dd:after{content:" ";display:table}.dl-horizontal dd:after{clear:both}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #63676a}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:9px 18px;margin:0 0 18px;font-size:15px;border-left:5px solid #5cb85c}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.5;color:#63676a}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #5cb85c;border-left:0;text-align:right}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:18px;font-style:normal;line-height:1.5}code,kbd,pre,samp{font-family:Consolas,monospace,Menlo,Monaco,"Courier New"}code{padding:2px 4px;font-size:90%;color:#1d1d1d;background-color:#63676a;white-space:nowrap;border-radius:3px}kbd{padding:2px 4px;font-size:90%;color:#adadad;background-color:#2b2b2b;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:11px;line-height:1.5;word-break:break-all;word-wrap:break-word;color:#1d1d1d;background-color:#63676a;border-radius:3px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:10px;padding-right:10px}.container:before,.container:after{content:" ";display:table}.container:after{clear:both}@media (min-width: 780px){.container{width:740px}}@media (min-width: 1200px){.container{width:960px}}@media (min-width: 1600px){.container{width:1160px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:10px;padding-right:10px}.container-fluid:before,.container-fluid:after{content:" ";display:table}.container-fluid:after{clear:both}.row{margin-left:-10px;margin-right:-10px}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:10px;padding-right:10px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333%}.col-xs-2{width:16.66667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333%}.col-xs-5{width:41.66667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333%}.col-xs-8{width:66.66667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333%}.col-xs-11{width:91.66667%}.col-xs-12{width:100%}.col-xs-pull-0{right:0%}.col-xs-pull-1{right:8.33333%}.col-xs-pull-2{right:16.66667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333%}.col-xs-pull-5{right:41.66667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333%}.col-xs-pull-8{right:66.66667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333%}.col-xs-pull-11{right:91.66667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:0%}.col-xs-push-1{left:8.33333%}.col-xs-push-2{left:16.66667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333%}.col-xs-push-5{left:41.66667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333%}.col-xs-push-8{left:66.66667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333%}.col-xs-push-11{left:91.66667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333%}.col-xs-offset-2{margin-left:16.66667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333%}.col-xs-offset-5{margin-left:41.66667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333%}.col-xs-offset-8{margin-left:66.66667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333%}.col-xs-offset-11{margin-left:91.66667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 780px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333%}.col-sm-2{width:16.66667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333%}.col-sm-5{width:41.66667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333%}.col-sm-8{width:66.66667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333%}.col-sm-11{width:91.66667%}.col-sm-12{width:100%}.col-sm-pull-0{right:0%}.col-sm-pull-1{right:8.33333%}.col-sm-pull-2{right:16.66667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333%}.col-sm-pull-5{right:41.66667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333%}.col-sm-pull-8{right:66.66667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333%}.col-sm-pull-11{right:91.66667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:0%}.col-sm-push-1{left:8.33333%}.col-sm-push-2{left:16.66667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333%}.col-sm-push-5{left:41.66667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333%}.col-sm-push-8{left:66.66667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333%}.col-sm-push-11{left:91.66667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333%}.col-sm-offset-2{margin-left:16.66667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333%}.col-sm-offset-5{margin-left:41.66667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333%}.col-sm-offset-8{margin-left:66.66667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333%}.col-sm-offset-11{margin-left:91.66667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333%}.col-md-2{width:16.66667%}.col-md-3{width:25%}.col-md-4{width:33.33333%}.col-md-5{width:41.66667%}.col-md-6{width:50%}.col-md-7{width:58.33333%}.col-md-8{width:66.66667%}.col-md-9{width:75%}.col-md-10{width:83.33333%}.col-md-11{width:91.66667%}.col-md-12{width:100%}.col-md-pull-0{right:0%}.col-md-pull-1{right:8.33333%}.col-md-pull-2{right:16.66667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333%}.col-md-pull-5{right:41.66667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333%}.col-md-pull-8{right:66.66667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333%}.col-md-pull-11{right:91.66667%}.col-md-pull-12{right:100%}.col-md-push-0{left:0%}.col-md-push-1{left:8.33333%}.col-md-push-2{left:16.66667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333%}.col-md-push-5{left:41.66667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333%}.col-md-push-8{left:66.66667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333%}.col-md-push-11{left:91.66667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333%}.col-md-offset-2{margin-left:16.66667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333%}.col-md-offset-5{margin-left:41.66667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333%}.col-md-offset-8{margin-left:66.66667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333%}.col-md-offset-11{margin-left:91.66667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1600px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333%}.col-lg-2{width:16.66667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333%}.col-lg-5{width:41.66667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333%}.col-lg-8{width:66.66667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333%}.col-lg-11{width:91.66667%}.col-lg-12{width:100%}.col-lg-pull-0{right:0%}.col-lg-pull-1{right:8.33333%}.col-lg-pull-2{right:16.66667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333%}.col-lg-pull-5{right:41.66667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333%}.col-lg-pull-8{right:66.66667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333%}.col-lg-pull-11{right:91.66667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:0%}.col-lg-push-1{left:8.33333%}.col-lg-push-2{left:16.66667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333%}.col-lg-push-5{left:41.66667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333%}.col-lg-push-8{left:66.66667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333%}.col-lg-push-11{left:91.66667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333%}.col-lg-offset-2{margin-left:16.66667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333%}.col-lg-offset-5{margin-left:41.66667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333%}.col-lg-offset-8{margin-left:66.66667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333%}.col-lg-offset-11{margin-left:91.66667%}.col-lg-offset-12{margin-left:100%}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:0;border-spacing:0}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.5;vertical-align:top;border-top:1px solid #313335}.table>thead>tr>th{vertical-align:bottom;border-bottom:0px solid #313335}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #313335}.table .table{background-color:#1d1d1d}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:2px}.table-bordered{border:1px solid #313335}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #313335}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#ecf3f8}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#ecf3f8}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#d9e7f1}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#d1e8d1}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#c0e0c0}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#abc9e2}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#98bcdc}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fdedd8}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#fbe3c0}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f6d1d0}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#f1bcba}@media (max-width: 779px){.table-responsive{width:100%;margin-bottom:13.5px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #313335;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:18px;font-size:18px;line-height:inherit;color:#313335;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}output{display:block;padding-top:7px;font-size:12px;line-height:1.5;color:#adadad}.form-control{display:block;width:100%;height:32px;padding:6px 12px;font-size:12px;line-height:1.5;color:#adadad;background-color:#313335;background-image:none;border:1px solid #63676a;border-radius:0px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-out 0.15s,box-shadow ease-out 0.15s;transition:border-color ease-out 0.15s,box-shadow ease-out 0.15s}.form-control:focus{border-color:#568a89;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(86,138,137,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(86,138,137,0.6)}.form-control::-moz-placeholder{color:#63676a;opacity:1}.form-control:-ms-input-placeholder{color:#63676a}.form-control::-webkit-input-placeholder{color:#63676a}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#adadad;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"]{line-height:32px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:18px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],fieldset[disabled] input[type="checkbox"],.radio[disabled],fieldset[disabled] .radio,.radio-inline[disabled],fieldset[disabled] .radio-inline,.checkbox[disabled],fieldset[disabled] .checkbox,.checkbox-inline[disabled],fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:28px;padding:5px 10px;font-size:11px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:28px;line-height:28px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:42px;padding:10px 16px;font-size:15px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:42px;line-height:42px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:40px}.has-feedback .form-control-feedback{position:absolute;top:23px;right:0;display:block;width:32px;height:32px;line-height:32px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#5cb85c}.has-success .form-control{border-color:#5cb85c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #a3d7a3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #a3d7a3}.has-success .select2-selection{border:1px solid !important;border-color:#5cb85c !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .input-group-addon{color:#5cb85c;border-color:#5cb85c;background-color:#d1e8d1}.has-success .form-control-feedback{color:#5cb85c}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#2b2b2b}.has-warning .form-control{border-color:#2b2b2b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #5e5e5e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #5e5e5e}.has-warning .select2-selection{border:1px solid !important;border-color:#2b2b2b !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .input-group-addon{color:#2b2b2b;border-color:#2b2b2b;background-color:#fdedd8}.has-warning .form-control-feedback{color:#2b2b2b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#d9534f}.has-error .form-control{border-color:#d9534f;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3}.has-error .select2-selection{border:1px solid !important;border-color:#d9534f !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .input-group-addon{color:#d9534f;border-color:#d9534f;background-color:#f6d1d0}.has-error .form-control-feedback{color:#d9534f}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#7c8184}@media (min-width: 780px){.form-inline .form-group,.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control,.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control,.navbar-form .input-group>.form-control{width:100%}.form-inline .control-label,.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.navbar-form .radio,.form-inline .checkbox,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type="radio"],.navbar-form .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback,.navbar-form .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:25px}.form-horizontal .form-group{margin-left:-10px;margin-right:-10px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{content:" ";display:table}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-control-static{padding-top:7px}@media (min-width: 780px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:10px}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:12px;line-height:1.5;border-radius:0px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.btn:hover,.btn:focus{color:#eaeaea;text-decoration:none;-webkit-box-shadow:none;box-shadow:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#eaeaea;background-color:#63676a;border-color:#575a5d}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active{color:#eaeaea;background-color:#777b7f;border-color:#74797c}.open .btn-default.dropdown-toggle{color:#eaeaea;background-color:#777b7f;border-color:#74797c}.btn-default:active,.btn-default.active{background-image:none}.open .btn-default.dropdown-toggle{background-image:none}.btn-default.disabled,.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled:active,.btn-default.disabled.active,.btn-default[disabled],.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled]:active,.btn-default[disabled].active,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default.active{background-color:#63676a;border-color:#575a5d}.btn-default .badge{color:#63676a;background-color:#eaeaea}.btn-primary{color:#eaeaea;background-color:#375959;border-color:#2d4949}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active{color:#eaeaea;background-color:#477272;border-color:#456f6f}.open .btn-primary.dropdown-toggle{color:#eaeaea;background-color:#477272;border-color:#456f6f}.btn-primary:active,.btn-primary.active{background-image:none}.open .btn-primary.dropdown-toggle{background-image:none}.btn-primary.disabled,.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled:active,.btn-primary.disabled.active,.btn-primary[disabled],.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled]:active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary.active{background-color:#375959;border-color:#2d4949}.btn-primary .badge{color:#375959;background-color:#eaeaea}.btn-success,.modal-content .pf-wizard-navigation li.active a:not(.btn-danger){color:#eaeaea;background-color:#4f9e4f;border-color:#478d47}.btn-success:hover,.modal-content .pf-wizard-navigation li.active a:hover:not(.btn-danger),.btn-success:focus,.modal-content .pf-wizard-navigation li.active a:focus:not(.btn-danger),.btn-success:active,.modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.btn-success.active,.modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger){color:#eaeaea;background-color:#64b264;border-color:#61b061}.open .btn-success.dropdown-toggle,.open .modal-content .pf-wizard-navigation li.active a.dropdown-toggle:not(.btn-danger),.modal-content .pf-wizard-navigation li.active .open a.dropdown-toggle:not(.btn-danger){color:#eaeaea;background-color:#64b264;border-color:#61b061}.btn-success:active,.modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.btn-success.active,.modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger){background-image:none}.open .btn-success.dropdown-toggle,.open .modal-content .pf-wizard-navigation li.active a.dropdown-toggle:not(.btn-danger),.modal-content .pf-wizard-navigation li.active .open a.dropdown-toggle:not(.btn-danger){background-image:none}.btn-success.disabled,.modal-content .pf-wizard-navigation li.active a.disabled:not(.btn-danger),.btn-success.disabled:hover,.modal-content .pf-wizard-navigation li.active a.disabled:hover:not(.btn-danger),.btn-success.disabled:focus,.modal-content .pf-wizard-navigation li.active a.disabled:focus:not(.btn-danger),.btn-success.disabled:active,.modal-content .pf-wizard-navigation li.active a.disabled:active:not(.btn-danger),.btn-success.disabled.active,.modal-content .pf-wizard-navigation li.active a.disabled.active:not(.btn-danger),.btn-success[disabled],.modal-content .pf-wizard-navigation li.active a[disabled]:not(.btn-danger),.btn-success[disabled]:hover,.modal-content .pf-wizard-navigation li.active a[disabled]:hover:not(.btn-danger),.btn-success[disabled]:focus,.modal-content .pf-wizard-navigation li.active a[disabled]:focus:not(.btn-danger),.btn-success[disabled]:active,.modal-content .pf-wizard-navigation li.active a[disabled]:active:not(.btn-danger),.btn-success[disabled].active,.modal-content .pf-wizard-navigation li.active a[disabled].active:not(.btn-danger),fieldset[disabled] .btn-success,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:not(.btn-danger),fieldset[disabled] .btn-success:hover,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:hover:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:hover:not(.btn-danger),fieldset[disabled] .btn-success:focus,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:focus:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:focus:not(.btn-danger),fieldset[disabled] .btn-success:active,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:active:not(.btn-danger),fieldset[disabled] .btn-success.active,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a.active:not(.btn-danger){background-color:#4f9e4f;border-color:#478d47}.btn-success .badge,.modal-content .pf-wizard-navigation li.active a:not(.btn-danger) .badge{color:#4f9e4f;background-color:#eaeaea}.btn-info{color:#eaeaea;background-color:#316490;border-color:#2b577d}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active{color:#eaeaea;background-color:#3b79ae;border-color:#3a76ab}.open .btn-info.dropdown-toggle{color:#eaeaea;background-color:#3b79ae;border-color:#3a76ab}.btn-info:active,.btn-info.active{background-image:none}.open .btn-info.dropdown-toggle{background-image:none}.btn-info.disabled,.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled:active,.btn-info.disabled.active,.btn-info[disabled],.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled]:active,.btn-info[disabled].active,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info.active{background-color:#316490;border-color:#2b577d}.btn-info .badge{color:#316490;background-color:#eaeaea}.btn-warning{color:#eaeaea;background-color:#c2760c;border-color:#aa670b}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active{color:#eaeaea;background-color:#e88d0e;border-color:#e48a0e}.open .btn-warning.dropdown-toggle{color:#eaeaea;background-color:#e88d0e;border-color:#e48a0e}.btn-warning:active,.btn-warning.active{background-image:none}.open .btn-warning.dropdown-toggle{background-image:none}.btn-warning.disabled,.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled:active,.btn-warning.disabled.active,.btn-warning[disabled],.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled]:active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning.active{background-color:#c2760c;border-color:#aa670b}.btn-warning .badge{color:#c2760c;background-color:#eaeaea}.btn-danger{color:#eaeaea;background-color:#a52521;border-color:#90201d}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active{color:#eaeaea;background-color:#c72d28;border-color:#c32c27}.open .btn-danger.dropdown-toggle{color:#eaeaea;background-color:#c72d28;border-color:#c32c27}.btn-danger:active,.btn-danger.active{background-image:none}.open .btn-danger.dropdown-toggle{background-image:none}.btn-danger.disabled,.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled:active,.btn-danger.disabled.active,.btn-danger[disabled],.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled]:active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger.active{background-color:#a52521;border-color:#90201d}.btn-danger .badge{color:#a52521;background-color:#eaeaea}.btn-link{color:#375959;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#1a2a2a;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#63676a;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:15px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:11px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:11px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;will-change:height;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@font-face{font-family:'Glyphicons Halflings';src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:12px;background-color:#adadad;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#63676a}.dropdown-menu>li>a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:1.5;color:#313335;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#1d1d1d;background-color:#63676a}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#adadad;text-decoration:none;outline:0;background-color:#375959}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#63676a}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:11px;line-height:1.5;color:#63676a}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width: 480px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:none}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{content:" ";display:table}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{content:" ";display:table}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:0px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:0px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:12px;font-weight:normal;line-height:1;color:#63676a;text-align:center;background-color:#2b2b2b;border:1px solid #63676a;border-radius:0px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:11px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:15px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav:before,.nav:after{content:" ";display:table}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:5px 6px 5px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#63676a}.nav>li.disabled>a{color:#63676a}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#63676a;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#63676a;border-color:#375959}.nav .nav-divider{height:1px;margin:5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #adadad}.nav-tabs>li{float:left;margin-bottom:-1px;margin-right:2px}.nav-tabs>li>a{line-height:1.5;border:1px solid transparent}.nav-tabs>li>a:hover{border-color:#adadad #adadad #adadad}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#313335;border:1px solid #adadad;border-bottom-color:transparent;cursor:default}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#375959}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 780px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:0px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 780px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:0px 0px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#1d1d1d}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:30px;margin-bottom:18px}.navbar:before,.navbar:after{content:" ";display:table}.navbar:after{clear:both}@media (min-width: 480px){.navbar{border-radius:0px}}.navbar-header:before,.navbar-header:after{content:" ";display:table}.navbar-header:after{clear:both}@media (min-width: 480px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:10px;padding-left:10px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{content:" ";display:table}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 480px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-10px;margin-left:-10px}@media (min-width: 480px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 480px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width: 480px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:6px 10px;font-size:1.2em;line-height:18px;height:30px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width: 480px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-10px}}.navbar-toggle{position:relative;float:right;margin-right:10px;margin-left:10px;padding:6px 12px;margin-top:-2px;margin-bottom:-2px;background-color:#313335;background-image:none;border:1px solid #a0a0a0;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px}.navbar-toggle:focus{outline:none}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 480px){.navbar-toggle{display:none}}.navbar-nav{margin:3px -10px;float:left;margin:0}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:18px}@media (max-width: 479px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:18px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}.navbar-nav>li{float:left;margin-right:2px}.navbar-nav>li>a{padding-top:6px;padding-bottom:6px}.navbar-nav.navbar-right:last-child{margin-right:-10px}@media (min-width: 480px){.navbar-left{float:left !important}.navbar-right{float:right !important}}.navbar-form{margin-left:-10px;margin-right:-10px;padding:10px 10px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:-1px;margin-bottom:-1px}@media (max-width: 479px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width: 480px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-10px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:-1px;margin-bottom:-1px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:1px;margin-bottom:1px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:4px;margin-bottom:4px}.navbar-text{margin-top:6px;margin-bottom:6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;float:left;margin-left:10px;margin-right:10px}.navbar-text.navbar-right:last-child{margin-right:0}.navbar-default{background:rgba(43,43,43,0.9);border-color:none}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-text{color:#63676a}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#6caead;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-nav>.active>a:hover{color:#6caead}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#3c3f41;background-color:transparent}.navbar-default .navbar-toggle{border-color:#477372;color:#2b2b2b;cursor:pointer}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{border-color:#518382;background-color:#3d4042}.navbar-default .navbar-toggle:hover .icon-bar,.navbar-default .navbar-toggle:focus .icon-bar{background-color:#568a89}.navbar-default .navbar-toggle .icon-bar{background-color:#477372}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:none}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:transparent;color:#568a89}@media (max-width: 479px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#6caead;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#3c3f41;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#6caead}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#63676a}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#63676a}.navbar-inverse .navbar-nav>li>a{color:#63676a}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#090909;color:#fff}@media (max-width: 479px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#63676a}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#63676a}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:0px 0px;margin-bottom:18px;list-style:none;background-color:none;border-radius:0px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/ ";padding:0 5px;color:#63676a}.breadcrumb>.active{color:#5cb85c}.pagination{display:inline-block;padding-left:0;margin:18px 0;border-radius:0px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.5;text-decoration:none;color:#375959;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:0px;border-top-left-radius:0px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:0px;border-top-right-radius:0px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{color:#1a2a2a;background-color:#adadad;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#375959;border-color:#375959;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#63676a;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:15px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:11px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:18px 0;list-style:none;text-align:center}.pager:before,.pager:after{content:" ";display:table}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#adadad}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#63676a;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:2px 5px;font-size:75%;font-weight:bold;line-height:1;color:#1d1d1d;text-align:center;white-space:nowrap;vertical-align:baseline;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#63676a}.label-default[href]:hover,.label-default[href]:focus{background-color:#4a4d50}.label-primary{background-color:#375959}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#243939}.label-success{background-color:#4f9e4f}.label-success[href]:hover,.label-success[href]:focus{background-color:#3e7c3e}.label-info{background-color:#316490}.label-info[href]:hover,.label-info[href]:focus{background-color:#244a6a}.label-warning{background-color:#e28a0d}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#b26d0a}.label-danger{background-color:#a52521}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#7b1b19}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:11px;font-weight:bold;color:#eaeaea;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#63676a;text-indent:initial;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#375959;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#adadad}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:18px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 780px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:54px}}.thumbnail{display:block;padding:4px;margin-bottom:18px;line-height:1.5;background-color:#1d1d1d;border:1px solid #ddd;border-radius:0px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-left:auto;margin-right:auto}.thumbnail .caption{padding:9px;color:#63676a}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#375959}.alert{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:0px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#d1e8d1;border-color:#4f9e4f;color:#2b2b2b}.alert-success hr{border-top-color:#478d47}.alert-success .alert-link{color:#121212}.alert-info{background-color:#abc9e2;border-color:#316490;color:#2b2b2b}.alert-info hr{border-top-color:#2b577d}.alert-info .alert-link{color:#121212}.alert-warning{background-color:#fdedd8;border-color:#e28a0d;color:#2b2b2b}.alert-warning hr{border-top-color:#ca7b0c}.alert-warning .alert-link{color:#121212}.alert-danger{background-color:#f6d1d0;border-color:#a52521;color:#2b2b2b}.alert-danger hr{border-top-color:#90201d}.alert-danger .alert-link{color:#121212}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f5f5f5;border-radius:0px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:11px;line-height:18px;color:#fff;text-align:center;background-color:#375959;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#4f9e4f}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-info{background-color:#316490}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-warning{background-color:#e28a0d}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-danger{background-color:#a52521}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:6px 8px;background-color:#adadad;border-bottom:1px solid #63676a;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.list-group-item:first-child{border-top-right-radius:0px;border-top-left-radius:0px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0px;border-bottom-left-radius:0px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#63676a;color:#1d1d1d}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#eaeaea;background-color:#375959;border-color:#375959}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#9bc1c1}.list-group-item-success{color:#2b2b2b;background-color:#d1e8d1}a.list-group-item-success{color:#2b2b2b}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#2b2b2b;background-color:#c0e0c0}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-info{color:#2b2b2b;background-color:#abc9e2}a.list-group-item-info{color:#2b2b2b}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#2b2b2b;background-color:#98bcdc}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-warning{color:#2b2b2b;background-color:#fdedd8}a.list-group-item-warning{color:#2b2b2b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#2b2b2b;background-color:#fbe3c0}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-danger{color:#2b2b2b;background-color:#f6d1d0}a.list-group-item-danger{color:#2b2b2b}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#2b2b2b;background-color:#f1bcba}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:18px;background-color:#3c3f41;border:1px solid transparent;border-radius:5px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{content:" ";display:table}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:4px;border-top-left-radius:4px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:13px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#353739;border-top:1px solid #313335;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:4px;border-top-left-radius:4px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:4px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:4px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:4px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:4px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #313335}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:18px}.panel-group .panel{margin-bottom:0;border-radius:5px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #313335}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #313335}.panel-default{border-color:#1d1d1d}.panel-default>.panel-heading{color:#63676a;background-color:#353739;border-color:#1d1d1d}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-primary{border-color:#1d1d1d}.panel-primary>.panel-heading{color:#1d1d1d;background-color:#375959;border-color:#1d1d1d}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-success{border-color:#1d1d1d}.panel-success>.panel-heading{color:#2b2b2b;background-color:#d1e8d1;border-color:#1d1d1d}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-info{border-color:#1d1d1d}.panel-info>.panel-heading{color:#2b2b2b;background-color:#abc9e2;border-color:#1d1d1d}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-warning{border-color:#1d1d1d}.panel-warning>.panel-heading{color:#1d1d1d;background-color:#e28a0d;border-color:#1d1d1d}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-danger{border-color:#1d1d1d}.panel-danger>.panel-heading{color:#2b2b2b;background-color:#f6d1d0;border-color:#1d1d1d}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.well{min-height:20px;padding:10px;margin-bottom:20px;background-color:#63676a;border:1px solid #313335;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;color:#2b2b2b;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well a{color:#6caead}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:18px;font-weight:bold;line-height:1;color:#a52521}.close:hover,.close:focus{color:#a52521;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:auto;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);-moz-transition:-moz-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);-o-transition:-o-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);transition:transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;width:auto;margin:10px;z-index:1050}.modal-content{position:relative;background-color:#3c3f41;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:none}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.3;filter:alpha(opacity=30)}.modal-header{padding:14px;border-bottom:1px solid #303234;min-height:15.5px;background-color:#353739}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.5}.modal-body{position:relative;padding:14px;color:#adadad}.modal-footer{padding:13px 14px 14px;text-align:right;border-top:1px solid #303234;background-color:#353739}.modal-footer:before,.modal-footer:after{content:" ";display:table}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width: 780px){.modal-dialog{width:700px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 1200px){.modal-lg{width:1100px}}.tooltip{position:absolute;z-index:1010;display:block;visibility:visible;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:0px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1030;display:none;max-width:276px;padding:1px;text-align:left;background-color:#3c3f41;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);white-space:normal}.popover.top{margin-top:-7px}.popover.right{margin-left:7px}.popover.bottom{margin-top:7px}.popover.left{margin-left:-7px}.popover-title{margin:0;padding:8px 14px;font-size:12px;font-weight:normal;line-height:18px;background-color:#353739;border-bottom:1px solid #282a2c;border-radius:5px 5px 0 0;color:#63676a}.popover-content{padding:7px 10px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:8px}.popover>.arrow:after{border-width:7px;content:""}.popover.top>.arrow{left:50%;margin-left:-8px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-8px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-7px;border-bottom-width:0;border-top-color:#63676a}.popover.right>.arrow{top:50%;left:-8px;margin-top:-8px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-7px;border-left-width:0;border-right-color:#63676a}.popover.bottom>.arrow{left:50%;margin-left:-8px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-8px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-7px;border-top-width:0;border-bottom-color:#63676a}.popover.left>.arrow{top:50%;right:-8px;margin-top:-8px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#63676a;bottom:-7px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.5) 0%), color-stop(rgba(0,0,0,0.0001) 100%));background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.0001) 0%), color-stop(rgba(0,0,0,0.5) 100%));background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:none;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:transparent}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 780px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}@media (max-width: 779px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (min-width: 780px) and (max-width: 1199px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 1200px) and (max-width: 1599px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 1600px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (max-width: 779px){.hidden-xs{display:none !important}}@media (min-width: 780px) and (max-width: 1199px){.hidden-sm{display:none !important}}@media (min-width: 1200px) and (max-width: 1599px){.hidden-md{display:none !important}}@media (min-width: 1600px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}@media print{.hidden-print{display:none !important}}/*! + */*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:transparent}body{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;font-size:12px;line-height:1.5;color:#63676a;background-color:#1d1d1d}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#375959;text-decoration:none}a:hover,a:focus{color:#1a2a2a;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89;text-decoration:none}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.5;background-color:#1d1d1d;border:1px solid #ddd;border-radius:0px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:18px;margin-bottom:18px;border:0;border-top:1px solid #adadad}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:700;line-height:1.1;color:inherit}h1 small,h1 .small,h2 small,h2 .small,h3 small,h3 .small,h4 small,h4 .small,h5 small,h5 .small,h6 small,h6 .small,.h1 small,.h1 .small,.h2 small,.h2 .small,.h3 small,.h3 .small,.h4 small,.h4 .small,.h5 small,.h5 .small,.h6 small,.h6 .small{font-weight:normal;line-height:1;color:#63676a}h1,.h1,h2,.h2,h3,.h3{margin-top:18px;margin-bottom:9px}h1 small,h1 .small,.h1 small,.h1 .small,h2 small,h2 .small,.h2 small,.h2 .small,h3 small,h3 .small,.h3 small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:9px;margin-bottom:9px}h4 small,h4 .small,.h4 small,.h4 .small,h5 small,h5 .small,.h5 small,.h5 .small,h6 small,h6 .small,.h6 small,.h6 .small{font-size:75%}h1,.h1{font-size:22px}h2,.h2{font-size:20px}h3,.h3{font-size:17px}h4,.h4{font-size:12px}h5,.h5{font-size:14px}h6,.h6{font-size:13px}p{margin:0 0 9px}.lead{margin-bottom:18px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width: 780px){.lead{font-size:16px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#63676a}.text-primary{color:#375959}a.text-primary:hover{color:#243939}.text-success{color:#2b2b2b}a.text-success:hover{color:#121212}.text-info{color:#2b2b2b}a.text-info:hover{color:#121212}.text-warning{color:#2b2b2b}a.text-warning:hover{color:#121212}.text-danger{color:#2b2b2b}a.text-danger:hover{color:#121212}.bg-primary{color:#fff}.bg-primary{background-color:#375959}a.bg-primary:hover{background-color:#243939}.bg-success{background-color:#d1e8d1}a.bg-success:hover{background-color:#afd7af}.bg-info{background-color:#abc9e2}a.bg-info:hover{background-color:#85b0d5}.bg-warning{background-color:#fdedd8}a.bg-warning:hover{background-color:#fad8a8}.bg-danger{background-color:#f6d1d0}a.bg-danger:hover{background-color:#eda7a5}.page-header{padding-bottom:8px;margin:36px 0 18px;border-bottom:1px solid #adadad}ul,ol{margin-top:0;margin-bottom:9px}ul ul,ul ol,ol ul,ol ol{margin-bottom:0}.list-unstyled,.list-inline{padding-left:0;list-style:none}.list-inline{margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:18px}dt,dd{line-height:1.5}dt{font-weight:bold}dd{margin-left:0}@media (min-width: 480px){.dl-horizontal dt{float:left;width:80px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:100px}.dl-horizontal dd:before,.dl-horizontal dd:after{content:" ";display:table}.dl-horizontal dd:after{clear:both}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #63676a}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:9px 18px;margin:0 0 18px;font-size:15px;border-left:5px solid #5cb85c}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.5;color:#63676a}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #5cb85c;border-left:0;text-align:right}.blockquote-reverse footer:before,.blockquote-reverse small:before,.blockquote-reverse .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,.blockquote-reverse small:after,.blockquote-reverse .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:18px;font-style:normal;line-height:1.5}code,kbd,pre,samp{font-family:Consolas,monospace,Menlo,Monaco,"Courier New"}code{padding:2px 4px;font-size:90%;color:#1d1d1d;background-color:#63676a;white-space:nowrap;border-radius:3px}kbd{padding:2px 4px;font-size:90%;color:#adadad;background-color:#2b2b2b;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:11px;line-height:1.5;word-break:break-all;word-wrap:break-word;color:#1d1d1d;background-color:#63676a;border-radius:3px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:10px;padding-right:10px}.container:before,.container:after{content:" ";display:table}.container:after{clear:both}@media (min-width: 780px){.container{width:740px}}@media (min-width: 1200px){.container{width:960px}}@media (min-width: 1600px){.container{width:1160px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:10px;padding-right:10px}.container-fluid:before,.container-fluid:after{content:" ";display:table}.container-fluid:after{clear:both}.row{margin-left:-10px;margin-right:-10px}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:10px;padding-right:10px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-1{width:8.33333%}.col-xs-2{width:16.66667%}.col-xs-3{width:25%}.col-xs-4{width:33.33333%}.col-xs-5{width:41.66667%}.col-xs-6{width:50%}.col-xs-7{width:58.33333%}.col-xs-8{width:66.66667%}.col-xs-9{width:75%}.col-xs-10{width:83.33333%}.col-xs-11{width:91.66667%}.col-xs-12{width:100%}.col-xs-pull-0{right:0%}.col-xs-pull-1{right:8.33333%}.col-xs-pull-2{right:16.66667%}.col-xs-pull-3{right:25%}.col-xs-pull-4{right:33.33333%}.col-xs-pull-5{right:41.66667%}.col-xs-pull-6{right:50%}.col-xs-pull-7{right:58.33333%}.col-xs-pull-8{right:66.66667%}.col-xs-pull-9{right:75%}.col-xs-pull-10{right:83.33333%}.col-xs-pull-11{right:91.66667%}.col-xs-pull-12{right:100%}.col-xs-push-0{left:0%}.col-xs-push-1{left:8.33333%}.col-xs-push-2{left:16.66667%}.col-xs-push-3{left:25%}.col-xs-push-4{left:33.33333%}.col-xs-push-5{left:41.66667%}.col-xs-push-6{left:50%}.col-xs-push-7{left:58.33333%}.col-xs-push-8{left:66.66667%}.col-xs-push-9{left:75%}.col-xs-push-10{left:83.33333%}.col-xs-push-11{left:91.66667%}.col-xs-push-12{left:100%}.col-xs-offset-0{margin-left:0%}.col-xs-offset-1{margin-left:8.33333%}.col-xs-offset-2{margin-left:16.66667%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-4{margin-left:33.33333%}.col-xs-offset-5{margin-left:41.66667%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-7{margin-left:58.33333%}.col-xs-offset-8{margin-left:66.66667%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-10{margin-left:83.33333%}.col-xs-offset-11{margin-left:91.66667%}.col-xs-offset-12{margin-left:100%}@media (min-width: 780px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.33333%}.col-sm-2{width:16.66667%}.col-sm-3{width:25%}.col-sm-4{width:33.33333%}.col-sm-5{width:41.66667%}.col-sm-6{width:50%}.col-sm-7{width:58.33333%}.col-sm-8{width:66.66667%}.col-sm-9{width:75%}.col-sm-10{width:83.33333%}.col-sm-11{width:91.66667%}.col-sm-12{width:100%}.col-sm-pull-0{right:0%}.col-sm-pull-1{right:8.33333%}.col-sm-pull-2{right:16.66667%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333%}.col-sm-pull-5{right:41.66667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.33333%}.col-sm-pull-8{right:66.66667%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333%}.col-sm-pull-11{right:91.66667%}.col-sm-pull-12{right:100%}.col-sm-push-0{left:0%}.col-sm-push-1{left:8.33333%}.col-sm-push-2{left:16.66667%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333%}.col-sm-push-5{left:41.66667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.33333%}.col-sm-push-8{left:66.66667%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333%}.col-sm-push-11{left:91.66667%}.col-sm-push-12{left:100%}.col-sm-offset-0{margin-left:0%}.col-sm-offset-1{margin-left:8.33333%}.col-sm-offset-2{margin-left:16.66667%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333%}.col-sm-offset-5{margin-left:41.66667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.33333%}.col-sm-offset-8{margin-left:66.66667%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333%}.col-sm-offset-11{margin-left:91.66667%}.col-sm-offset-12{margin-left:100%}}@media (min-width: 1200px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-1{width:8.33333%}.col-md-2{width:16.66667%}.col-md-3{width:25%}.col-md-4{width:33.33333%}.col-md-5{width:41.66667%}.col-md-6{width:50%}.col-md-7{width:58.33333%}.col-md-8{width:66.66667%}.col-md-9{width:75%}.col-md-10{width:83.33333%}.col-md-11{width:91.66667%}.col-md-12{width:100%}.col-md-pull-0{right:0%}.col-md-pull-1{right:8.33333%}.col-md-pull-2{right:16.66667%}.col-md-pull-3{right:25%}.col-md-pull-4{right:33.33333%}.col-md-pull-5{right:41.66667%}.col-md-pull-6{right:50%}.col-md-pull-7{right:58.33333%}.col-md-pull-8{right:66.66667%}.col-md-pull-9{right:75%}.col-md-pull-10{right:83.33333%}.col-md-pull-11{right:91.66667%}.col-md-pull-12{right:100%}.col-md-push-0{left:0%}.col-md-push-1{left:8.33333%}.col-md-push-2{left:16.66667%}.col-md-push-3{left:25%}.col-md-push-4{left:33.33333%}.col-md-push-5{left:41.66667%}.col-md-push-6{left:50%}.col-md-push-7{left:58.33333%}.col-md-push-8{left:66.66667%}.col-md-push-9{left:75%}.col-md-push-10{left:83.33333%}.col-md-push-11{left:91.66667%}.col-md-push-12{left:100%}.col-md-offset-0{margin-left:0%}.col-md-offset-1{margin-left:8.33333%}.col-md-offset-2{margin-left:16.66667%}.col-md-offset-3{margin-left:25%}.col-md-offset-4{margin-left:33.33333%}.col-md-offset-5{margin-left:41.66667%}.col-md-offset-6{margin-left:50%}.col-md-offset-7{margin-left:58.33333%}.col-md-offset-8{margin-left:66.66667%}.col-md-offset-9{margin-left:75%}.col-md-offset-10{margin-left:83.33333%}.col-md-offset-11{margin-left:91.66667%}.col-md-offset-12{margin-left:100%}}@media (min-width: 1600px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.33333%}.col-lg-2{width:16.66667%}.col-lg-3{width:25%}.col-lg-4{width:33.33333%}.col-lg-5{width:41.66667%}.col-lg-6{width:50%}.col-lg-7{width:58.33333%}.col-lg-8{width:66.66667%}.col-lg-9{width:75%}.col-lg-10{width:83.33333%}.col-lg-11{width:91.66667%}.col-lg-12{width:100%}.col-lg-pull-0{right:0%}.col-lg-pull-1{right:8.33333%}.col-lg-pull-2{right:16.66667%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333%}.col-lg-pull-5{right:41.66667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.33333%}.col-lg-pull-8{right:66.66667%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333%}.col-lg-pull-11{right:91.66667%}.col-lg-pull-12{right:100%}.col-lg-push-0{left:0%}.col-lg-push-1{left:8.33333%}.col-lg-push-2{left:16.66667%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333%}.col-lg-push-5{left:41.66667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.33333%}.col-lg-push-8{left:66.66667%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333%}.col-lg-push-11{left:91.66667%}.col-lg-push-12{left:100%}.col-lg-offset-0{margin-left:0%}.col-lg-offset-1{margin-left:8.33333%}.col-lg-offset-2{margin-left:16.66667%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333%}.col-lg-offset-5{margin-left:41.66667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.33333%}.col-lg-offset-8{margin-left:66.66667%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333%}.col-lg-offset-11{margin-left:91.66667%}.col-lg-offset-12{margin-left:100%}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:0;border-spacing:0}.table>thead>tr>th,.table>thead>tr>td,.table>tbody>tr>th,.table>tbody>tr>td,.table>tfoot>tr>th,.table>tfoot>tr>td{padding:8px;line-height:1.5;vertical-align:top;border-top:1px solid #313335}.table>thead>tr>th{vertical-align:bottom;border-bottom:0px solid #313335}.table>caption+thead>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>th,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #313335}.table .table{background-color:#1d1d1d}.table-condensed>thead>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>tfoot>tr>td{padding:2px}.table-bordered{border:1px solid #313335}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>td{border:1px solid #313335}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#ecf3f8}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>thead>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th{background-color:#ecf3f8}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#d9e7f1}.table>thead>tr>td.success,.table>thead>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th{background-color:#d1e8d1}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#c0e0c0}.table>thead>tr>td.info,.table>thead>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th{background-color:#abc9e2}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#98bcdc}.table>thead>tr>td.warning,.table>thead>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th{background-color:#fdedd8}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#fbe3c0}.table>thead>tr>td.danger,.table>thead>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th{background-color:#f6d1d0}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#f1bcba}@media (max-width: 779px){.table-responsive{width:100%;margin-bottom:13.5px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #313335;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:18px;font-size:18px;line-height:inherit;color:#313335;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}output{display:block;padding-top:7px;font-size:12px;line-height:1.5;color:#adadad}.form-control{display:block;width:100%;height:32px;padding:6px 12px;font-size:12px;line-height:1.5;color:#adadad;background-color:#313335;background-image:none;border:1px solid #63676a;border-radius:0px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-out 0.15s,box-shadow ease-out 0.15s;transition:border-color ease-out 0.15s,box-shadow ease-out 0.15s}.form-control:focus{border-color:#568a89;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(86,138,137,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(86,138,137,0.6)}.form-control::-moz-placeholder{color:#63676a;opacity:1}.form-control:-ms-input-placeholder{color:#63676a}.form-control::-webkit-input-placeholder{color:#63676a}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#3c3f41;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"]{line-height:32px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:18px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],fieldset[disabled] input[type="checkbox"],.radio[disabled],fieldset[disabled] .radio,.radio-inline[disabled],fieldset[disabled] .radio-inline,.checkbox[disabled],fieldset[disabled] .checkbox,.checkbox-inline[disabled],fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:28px;padding:5px 10px;font-size:11px;line-height:1.5;border-radius:3px}select.input-sm,.input-group-sm>select.form-control,.input-group-sm>select.input-group-addon,.input-group-sm>.input-group-btn>select.btn{height:28px;line-height:28px}textarea.input-sm,.input-group-sm>textarea.form-control,.input-group-sm>textarea.input-group-addon,.input-group-sm>.input-group-btn>textarea.btn,select[multiple].input-sm,.input-group-sm>select[multiple].form-control,.input-group-sm>select[multiple].input-group-addon,.input-group-sm>.input-group-btn>select[multiple].btn{height:auto}.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:42px;padding:10px 16px;font-size:15px;line-height:1.33;border-radius:6px}select.input-lg,.input-group-lg>select.form-control,.input-group-lg>select.input-group-addon,.input-group-lg>.input-group-btn>select.btn{height:42px;line-height:42px}textarea.input-lg,.input-group-lg>textarea.form-control,.input-group-lg>textarea.input-group-addon,.input-group-lg>.input-group-btn>textarea.btn,select[multiple].input-lg,.input-group-lg>select[multiple].form-control,.input-group-lg>select[multiple].input-group-addon,.input-group-lg>.input-group-btn>select[multiple].btn{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:40px}.has-feedback .form-control-feedback{position:absolute;top:23px;right:0;display:block;width:32px;height:32px;line-height:32px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#5cb85c}.has-success .form-control{border-color:#5cb85c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #a3d7a3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #a3d7a3}.has-success .select2-selection{border:1px solid !important;border-color:#5cb85c !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .input-group-addon{color:#5cb85c;border-color:#5cb85c;background-color:#d1e8d1}.has-success .form-control-feedback{color:#5cb85c}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#2b2b2b}.has-warning .form-control{border-color:#2b2b2b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #5e5e5e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #5e5e5e}.has-warning .select2-selection{border:1px solid !important;border-color:#2b2b2b !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .input-group-addon{color:#2b2b2b;border-color:#2b2b2b;background-color:#fdedd8}.has-warning .form-control-feedback{color:#2b2b2b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#d9534f}.has-error .form-control{border-color:#d9534f;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3}.has-error .select2-selection{border:1px solid !important;border-color:#d9534f !important;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .input-group-addon{color:#d9534f;border-color:#d9534f;background-color:#f6d1d0}.has-error .form-control-feedback{color:#d9534f}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#7c8184}@media (min-width: 780px){.form-inline .form-group,.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control,.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control,.navbar-form .input-group>.form-control{width:100%}.form-inline .control-label,.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.navbar-form .radio,.form-inline .checkbox,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type="radio"],.navbar-form .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback,.navbar-form .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:25px}.form-horizontal .form-group{margin-left:-10px;margin-right:-10px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{content:" ";display:table}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-control-static{padding-top:7px}@media (min-width: 780px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:10px}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:12px;line-height:1.5;border-radius:0px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.btn:hover,.btn:focus{color:#eaeaea;text-decoration:none;-webkit-box-shadow:none;box-shadow:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#eaeaea;background-color:#63676a;border-color:#575a5d}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active{color:#eaeaea;background-color:#777b7f;border-color:#74797c}.open .btn-default.dropdown-toggle{color:#eaeaea;background-color:#777b7f;border-color:#74797c}.btn-default:active,.btn-default.active{background-image:none}.open .btn-default.dropdown-toggle{background-image:none}.btn-default.disabled,.btn-default.disabled:hover,.btn-default.disabled:focus,.btn-default.disabled:active,.btn-default.disabled.active,.btn-default[disabled],.btn-default[disabled]:hover,.btn-default[disabled]:focus,.btn-default[disabled]:active,.btn-default[disabled].active,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default:hover,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default.active{background-color:#63676a;border-color:#575a5d}.btn-default .badge{color:#63676a;background-color:#eaeaea}.btn-primary{color:#eaeaea;background-color:#375959;border-color:#2d4949}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active{color:#eaeaea;background-color:#477272;border-color:#456f6f}.open .btn-primary.dropdown-toggle{color:#eaeaea;background-color:#477272;border-color:#456f6f}.btn-primary:active,.btn-primary.active{background-image:none}.open .btn-primary.dropdown-toggle{background-image:none}.btn-primary.disabled,.btn-primary.disabled:hover,.btn-primary.disabled:focus,.btn-primary.disabled:active,.btn-primary.disabled.active,.btn-primary[disabled],.btn-primary[disabled]:hover,.btn-primary[disabled]:focus,.btn-primary[disabled]:active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary:hover,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary.active{background-color:#375959;border-color:#2d4949}.btn-primary .badge{color:#375959;background-color:#eaeaea}.btn-success,.modal-content .pf-wizard-navigation li.active a:not(.btn-danger){color:#eaeaea;background-color:#4f9e4f;border-color:#478d47}.btn-success:hover,.modal-content .pf-wizard-navigation li.active a:hover:not(.btn-danger),.btn-success:focus,.modal-content .pf-wizard-navigation li.active a:focus:not(.btn-danger),.btn-success:active,.modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.btn-success.active,.modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger){color:#eaeaea;background-color:#64b264;border-color:#61b061}.open .btn-success.dropdown-toggle,.open .modal-content .pf-wizard-navigation li.active a.dropdown-toggle:not(.btn-danger),.modal-content .pf-wizard-navigation li.active .open a.dropdown-toggle:not(.btn-danger){color:#eaeaea;background-color:#64b264;border-color:#61b061}.btn-success:active,.modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.btn-success.active,.modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger){background-image:none}.open .btn-success.dropdown-toggle,.open .modal-content .pf-wizard-navigation li.active a.dropdown-toggle:not(.btn-danger),.modal-content .pf-wizard-navigation li.active .open a.dropdown-toggle:not(.btn-danger){background-image:none}.btn-success.disabled,.modal-content .pf-wizard-navigation li.active a.disabled:not(.btn-danger),.btn-success.disabled:hover,.modal-content .pf-wizard-navigation li.active a.disabled:hover:not(.btn-danger),.btn-success.disabled:focus,.modal-content .pf-wizard-navigation li.active a.disabled:focus:not(.btn-danger),.btn-success.disabled:active,.modal-content .pf-wizard-navigation li.active a.disabled:active:not(.btn-danger),.btn-success.disabled.active,.modal-content .pf-wizard-navigation li.active a.disabled.active:not(.btn-danger),.btn-success[disabled],.modal-content .pf-wizard-navigation li.active a[disabled]:not(.btn-danger),.btn-success[disabled]:hover,.modal-content .pf-wizard-navigation li.active a[disabled]:hover:not(.btn-danger),.btn-success[disabled]:focus,.modal-content .pf-wizard-navigation li.active a[disabled]:focus:not(.btn-danger),.btn-success[disabled]:active,.modal-content .pf-wizard-navigation li.active a[disabled]:active:not(.btn-danger),.btn-success[disabled].active,.modal-content .pf-wizard-navigation li.active a[disabled].active:not(.btn-danger),fieldset[disabled] .btn-success,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:not(.btn-danger),fieldset[disabled] .btn-success:hover,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:hover:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:hover:not(.btn-danger),fieldset[disabled] .btn-success:focus,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:focus:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:focus:not(.btn-danger),fieldset[disabled] .btn-success:active,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a:active:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a:active:not(.btn-danger),fieldset[disabled] .btn-success.active,fieldset[disabled] .modal-content .pf-wizard-navigation li.active a.active:not(.btn-danger),.modal-content .pf-wizard-navigation li.active fieldset[disabled] a.active:not(.btn-danger){background-color:#4f9e4f;border-color:#478d47}.btn-success .badge,.modal-content .pf-wizard-navigation li.active a:not(.btn-danger) .badge{color:#4f9e4f;background-color:#eaeaea}.btn-info{color:#eaeaea;background-color:#316490;border-color:#2b577d}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active{color:#eaeaea;background-color:#3b79ae;border-color:#3a76ab}.open .btn-info.dropdown-toggle{color:#eaeaea;background-color:#3b79ae;border-color:#3a76ab}.btn-info:active,.btn-info.active{background-image:none}.open .btn-info.dropdown-toggle{background-image:none}.btn-info.disabled,.btn-info.disabled:hover,.btn-info.disabled:focus,.btn-info.disabled:active,.btn-info.disabled.active,.btn-info[disabled],.btn-info[disabled]:hover,.btn-info[disabled]:focus,.btn-info[disabled]:active,.btn-info[disabled].active,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info:hover,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info.active{background-color:#316490;border-color:#2b577d}.btn-info .badge{color:#316490;background-color:#eaeaea}.btn-warning{color:#eaeaea;background-color:#c2760c;border-color:#aa670b}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active{color:#eaeaea;background-color:#e88d0e;border-color:#e48a0e}.open .btn-warning.dropdown-toggle{color:#eaeaea;background-color:#e88d0e;border-color:#e48a0e}.btn-warning:active,.btn-warning.active{background-image:none}.open .btn-warning.dropdown-toggle{background-image:none}.btn-warning.disabled,.btn-warning.disabled:hover,.btn-warning.disabled:focus,.btn-warning.disabled:active,.btn-warning.disabled.active,.btn-warning[disabled],.btn-warning[disabled]:hover,.btn-warning[disabled]:focus,.btn-warning[disabled]:active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning:hover,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning.active{background-color:#c2760c;border-color:#aa670b}.btn-warning .badge{color:#c2760c;background-color:#eaeaea}.btn-danger{color:#eaeaea;background-color:#a52521;border-color:#90201d}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active{color:#eaeaea;background-color:#c72d28;border-color:#c32c27}.open .btn-danger.dropdown-toggle{color:#eaeaea;background-color:#c72d28;border-color:#c32c27}.btn-danger:active,.btn-danger.active{background-image:none}.open .btn-danger.dropdown-toggle{background-image:none}.btn-danger.disabled,.btn-danger.disabled:hover,.btn-danger.disabled:focus,.btn-danger.disabled:active,.btn-danger.disabled.active,.btn-danger[disabled],.btn-danger[disabled]:hover,.btn-danger[disabled]:focus,.btn-danger[disabled]:active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger:hover,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger.active{background-color:#a52521;border-color:#90201d}.btn-danger .badge{color:#a52521;background-color:#eaeaea}.btn-link{color:#375959;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#1a2a2a;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:hover,fieldset[disabled] .btn-link:focus{color:#63676a;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:15px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:11px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:11px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;will-change:height;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@font-face{font-family:'Glyphicons Halflings';src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:12px;background-color:#adadad;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#63676a}.dropdown-menu>li>a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:1.5;color:#313335;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#1d1d1d;background-color:#63676a}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#adadad;text-decoration:none;outline:0;background-color:#375959}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#63676a}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:11px;line-height:1.5;color:#63676a}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width: 480px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:none}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar:before,.btn-toolbar:after{content:" ";display:table}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{content:" ";display:table}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:0px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:0px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:12px;font-weight:normal;line-height:1;color:#63676a;text-align:center;background-color:#2b2b2b;border:1px solid #63676a;border-radius:0px}.input-group-addon.input-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:5px 10px;font-size:11px;border-radius:3px}.input-group-addon.input-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:10px 16px;font-size:15px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav:before,.nav:after{content:" ";display:table}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:5px 6px 5px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#63676a}.nav>li.disabled>a{color:#63676a}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#63676a;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#63676a;border-color:#375959}.nav .nav-divider{height:1px;margin:5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #adadad}.nav-tabs>li{float:left;margin-bottom:-1px;margin-right:2px}.nav-tabs>li>a{line-height:1.5;border:1px solid transparent}.nav-tabs>li>a:hover{border-color:#adadad #adadad #adadad}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#313335;border:1px solid #adadad;border-bottom-color:transparent;cursor:default}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#375959}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified,.nav-tabs.nav-justified{width:100%}.nav-justified>li,.nav-tabs.nav-justified>li{float:none}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width: 780px){.nav-justified>li,.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a,.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified,.nav-tabs.nav-justified{border-bottom:0}.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:0px}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width: 780px){.nav-tabs-justified>li>a,.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:0px 0px 0 0}.nav-tabs-justified>.active>a,.nav-tabs.nav-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#1d1d1d}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:30px;margin-bottom:18px}.navbar:before,.navbar:after{content:" ";display:table}.navbar:after{clear:both}@media (min-width: 480px){.navbar{border-radius:0px}}.navbar-header:before,.navbar-header:after{content:" ";display:table}.navbar-header:after{clear:both}@media (min-width: 480px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:10px;padding-left:10px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{content:" ";display:table}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media (min-width: 480px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:-10px;margin-left:-10px}@media (min-width: 480px){.container>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-header,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width: 480px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width: 480px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:6px 10px;font-size:1.2em;line-height:18px;height:30px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width: 480px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-10px}}.navbar-toggle{position:relative;float:right;margin-right:10px;margin-left:10px;padding:6px 12px;margin-top:-2px;margin-bottom:-2px;background-color:#313335;background-image:none;border:1px solid #a0a0a0;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px}.navbar-toggle:focus{outline:none}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width: 480px){.navbar-toggle{display:none}}.navbar-nav{margin:3px -10px;float:left;margin:0}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:18px}@media (max-width: 479px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:18px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}.navbar-nav>li{float:left;margin-right:2px}.navbar-nav>li>a{padding-top:6px;padding-bottom:6px}.navbar-nav.navbar-right:last-child{margin-right:-10px}@media (min-width: 480px){.navbar-left{float:left !important}.navbar-right{float:right !important}}.navbar-form{margin-left:-10px;margin-right:-10px;padding:10px 10px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:-1px;margin-bottom:-1px}@media (max-width: 479px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width: 480px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-10px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:-1px;margin-bottom:-1px}.navbar-btn.btn-sm,.btn-group-sm>.navbar-btn.btn{margin-top:1px;margin-bottom:1px}.navbar-btn.btn-xs,.btn-group-xs>.navbar-btn.btn{margin-top:4px;margin-bottom:4px}.navbar-text{margin-top:6px;margin-bottom:6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;float:left;margin-left:10px;margin-right:10px}.navbar-text.navbar-right:last-child{margin-right:0}.navbar-default{background:rgba(43,43,43,0.9);border-color:none}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-text{color:#63676a}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#6caead;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-nav>.active>a:hover{color:#6caead}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#3c3f41;background-color:transparent}.navbar-default .navbar-toggle{border-color:#477372;color:#2b2b2b;cursor:pointer}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{border-color:#518382;background-color:#3d4042}.navbar-default .navbar-toggle:hover .icon-bar,.navbar-default .navbar-toggle:focus .icon-bar{background-color:#568a89}.navbar-default .navbar-toggle .icon-bar{background-color:#477372}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:none}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:transparent;color:#568a89}@media (max-width: 479px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#6caead;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#568a89;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#3c3f41;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#6caead}.navbar-inverse{background-color:#222;border-color:#090909}.navbar-inverse .navbar-brand{color:#63676a}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#63676a}.navbar-inverse .navbar-nav>li>a{color:#63676a}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#090909;color:#fff}@media (max-width: 479px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#63676a}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#090909}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#63676a}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:0px 0px;margin-bottom:18px;list-style:none;background-color:none;border-radius:0px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/ ";padding:0 5px;color:#63676a}.breadcrumb>.active{color:#5cb85c}.pagination{display:inline-block;padding-left:0;margin:18px 0;border-radius:0px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.5;text-decoration:none;color:#375959;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:0px;border-top-left-radius:0px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:0px;border-top-right-radius:0px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>li>span:hover,.pagination>li>span:focus{color:#1a2a2a;background-color:#adadad;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:hover,.pagination>.active>a:focus,.pagination>.active>span,.pagination>.active>span:hover,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#375959;border-color:#375959;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#63676a;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:15px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:11px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:18px 0;list-style:none;text-align:center}.pager:before,.pager:after{content:" ";display:table}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#adadad}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#63676a;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:2px 5px;font-size:75%;font-weight:bold;line-height:1;color:#1d1d1d;text-align:center;white-space:nowrap;vertical-align:baseline;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#63676a}.label-default[href]:hover,.label-default[href]:focus{background-color:#4a4d50}.label-primary{background-color:#375959}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#243939}.label-success{background-color:#4f9e4f}.label-success[href]:hover,.label-success[href]:focus{background-color:#3e7c3e}.label-info{background-color:#316490}.label-info[href]:hover,.label-info[href]:focus{background-color:#244a6a}.label-warning{background-color:#e28a0d}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#b26d0a}.label-danger{background-color:#a52521}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#7b1b19}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:11px;font-weight:bold;color:#eaeaea;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#63676a;text-indent:initial;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#375959;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#adadad}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:18px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width: 780px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:54px}}.thumbnail{display:block;padding:4px;margin-bottom:18px;line-height:1.5;background-color:#1d1d1d;border:1px solid #ddd;border-radius:0px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;max-width:100%;height:auto;margin-left:auto;margin-right:auto}.thumbnail .caption{padding:9px;color:#63676a}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#375959}.alert{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:0px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#d1e8d1;border-color:#4f9e4f;color:#2b2b2b}.alert-success hr{border-top-color:#478d47}.alert-success .alert-link{color:#121212}.alert-info{background-color:#abc9e2;border-color:#316490;color:#2b2b2b}.alert-info hr{border-top-color:#2b577d}.alert-info .alert-link{color:#121212}.alert-warning{background-color:#fdedd8;border-color:#e28a0d;color:#2b2b2b}.alert-warning hr{border-top-color:#ca7b0c}.alert-warning .alert-link{color:#121212}.alert-danger{background-color:#f6d1d0;border-color:#a52521;color:#2b2b2b}.alert-danger hr{border-top-color:#90201d}.alert-danger .alert-link{color:#121212}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f5f5f5;border-radius:0px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:11px;line-height:18px;color:#fff;text-align:center;background-color:#375959;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#4f9e4f}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-info{background-color:#316490}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-warning{background-color:#e28a0d}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-danger{background-color:#a52521}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:6px 8px;background-color:#adadad;border-bottom:1px solid #63676a;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.list-group-item:first-child{border-top-right-radius:0px;border-top-left-radius:0px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0px;border-bottom-left-radius:0px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#63676a;color:#1d1d1d}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#eaeaea;background-color:#375959;border-color:#375959}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#9bc1c1}.list-group-item-success{color:#2b2b2b;background-color:#d1e8d1}a.list-group-item-success{color:#2b2b2b}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#2b2b2b;background-color:#c0e0c0}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-info{color:#2b2b2b;background-color:#abc9e2}a.list-group-item-info{color:#2b2b2b}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#2b2b2b;background-color:#98bcdc}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-warning{color:#2b2b2b;background-color:#fdedd8}a.list-group-item-warning{color:#2b2b2b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#2b2b2b;background-color:#fbe3c0}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-danger{color:#2b2b2b;background-color:#f6d1d0}a.list-group-item-danger{color:#2b2b2b}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#2b2b2b;background-color:#f1bcba}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#2b2b2b;border-color:#2b2b2b}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:18px;background-color:#3c3f41;border:1px solid transparent;border-radius:5px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{content:" ";display:table}.panel-body:after{clear:both}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:4px;border-top-left-radius:4px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:13px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#353739;border-top:1px solid #313335;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:4px;border-top-left-radius:4px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:4px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:4px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:4px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:4px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #313335}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:18px}.panel-group .panel{margin-bottom:0;border-radius:5px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #313335}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #313335}.panel-default{border-color:#1d1d1d}.panel-default>.panel-heading{color:#63676a;background-color:#353739;border-color:#1d1d1d}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-primary{border-color:#1d1d1d}.panel-primary>.panel-heading{color:#1d1d1d;background-color:#375959;border-color:#1d1d1d}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-success{border-color:#1d1d1d}.panel-success>.panel-heading{color:#2b2b2b;background-color:#d1e8d1;border-color:#1d1d1d}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-info{border-color:#1d1d1d}.panel-info>.panel-heading{color:#2b2b2b;background-color:#abc9e2;border-color:#1d1d1d}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-warning{border-color:#1d1d1d}.panel-warning>.panel-heading{color:#1d1d1d;background-color:#e28a0d;border-color:#1d1d1d}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.panel-danger{border-color:#1d1d1d}.panel-danger>.panel-heading{color:#2b2b2b;background-color:#f6d1d0;border-color:#1d1d1d}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#1d1d1d}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#1d1d1d}.well{min-height:20px;padding:10px;margin-bottom:20px;background-color:#63676a;border:1px solid #313335;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;color:#2b2b2b;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well a{color:#6caead}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:18px;font-weight:bold;line-height:1;color:#a52521}.close:hover,.close:focus{color:#a52521;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:auto;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);-moz-transition:-moz-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);-o-transition:-o-transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);transition:transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;width:auto;margin:10px;z-index:1050}.modal-content{position:relative;background-color:#3c3f41;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:none}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.3;filter:alpha(opacity=30)}.modal-header{padding:14px;border-bottom:1px solid #303234;min-height:15.5px;background-color:#353739}.modal-header .close,.modal-body .close{padding:0 5px;font-size:22px;margin-top:-2px}.modal-title{margin:0;line-height:1.5}.modal-body{position:relative;padding:14px;color:#adadad}.modal-footer{padding:13px 14px 14px;text-align:right;border-top:1px solid #303234;background-color:#353739}.modal-footer:before,.modal-footer:after{content:" ";display:table}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width: 780px){.modal-dialog{width:700px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width: 1200px){.modal-lg{width:1100px}}.tooltip{position:absolute;z-index:1010;display:block;visibility:visible;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:0px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1030;display:none;max-width:276px;padding:1px;text-align:left;background-color:#3c3f41;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);white-space:normal}.popover.top{margin-top:-7px}.popover.right{margin-left:7px}.popover.bottom{margin-top:7px}.popover.left{margin-left:-7px}.popover-title{margin:0;padding:8px 14px;font-size:12px;font-weight:normal;line-height:18px;background-color:#353739;border-bottom:1px solid #282a2c;border-radius:5px 5px 0 0;color:#63676a}.popover-content{padding:7px 10px}.popover-content pre{margin-bottom:5px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:8px}.popover>.arrow:after{border-width:7px;content:""}.popover.top>.arrow{left:50%;margin-left:-8px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-8px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-7px;border-bottom-width:0;border-top-color:#63676a}.popover.right>.arrow{top:50%;left:-8px;margin-top:-8px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-7px;border-left-width:0;border-right-color:#63676a}.popover.bottom>.arrow{left:50%;margin-left:-8px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-8px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-7px;border-top-width:0;border-bottom-color:#63676a}.popover.left>.arrow{top:50%;right:-8px;margin-top:-8px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#63676a;bottom:-7px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.5) 0%), color-stop(rgba(0,0,0,0.0001) 100%));background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, color-stop(rgba(0,0,0,0.0001) 0%), color-stop(rgba(0,0,0,0.5) 100%));background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:none;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:transparent}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width: 780px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}@media (max-width: 779px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (min-width: 780px) and (max-width: 1199px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width: 1200px) and (max-width: 1599px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width: 1600px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (max-width: 779px){.hidden-xs{display:none !important}}@media (min-width: 780px) and (max-width: 1199px){.hidden-sm{display:none !important}}@media (min-width: 1200px) and (max-width: 1599px){.hidden-md{display:none !important}}@media (min-width: 1600px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}@media print{.hidden-print{display:none !important}}/*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.pf-landing .pf-landing-list li>i{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw,.pf-landing .pf-landing-list li>i{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li,.pf-landing .pf-landing-list li>i{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg,.pf-landing .pf-landing-list li>i.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.pf-landing .pf-landing-list li>i.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.pf-landing .pf-landing-list li>i.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pf-landing .pf-landing-list li>i.pull-left{margin-right:.3em}.fa.pull-right,.pf-landing .pf-landing-list li>i.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before,.pf-landing .pf-landing-list li>i:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.mCustomScrollbar{touch-action:none}.mCustomScrollbar.mCS_no_scrollbar{touch-action:auto}.mCustomScrollBox{position:relative;overflow:hidden;height:100%;max-width:100%;outline:none;direction:ltr}.mCSB_container{overflow:hidden;width:auto;height:auto}.mCSB_inside>.mCSB_container{margin-right:30px}.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container{margin-right:0;margin-left:30px}.mCS-dir-rtl>.mCSB_inside>.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-left:0}.mCSB_scrollTools{position:absolute;width:16px;height:auto;left:auto;top:0;right:0;bottom:0;opacity:0.75;filter:"alpha(opacity=75)"}.mCSB_outside+.mCSB_scrollTools{right:-26px}.mCS-dir-rtl>.mCSB_inside>.mCSB_scrollTools,.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{right:auto;left:0}.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{left:-26px}.mCSB_scrollTools .mCSB_draggerContainer{position:absolute;top:0;left:0;bottom:0;right:0;height:auto}.mCSB_scrollTools a+.mCSB_draggerContainer{margin:20px 0}.mCSB_scrollTools .mCSB_draggerRail{width:2px;height:100%;margin:0 auto;border-radius:16px;background-color:#000;background-color:rgba(0,0,0,0.4);filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_dragger{cursor:pointer;width:100%;height:30px;z-index:1}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{position:relative;width:4px;height:100%;margin:0 auto;border-radius:16px;text-align:center;background-color:#fff;background-color:rgba(255,255,255,0.75);filter:"alpha(opacity=75)"}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:12px}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:8px}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown{display:block;position:absolute;height:20px;width:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools .mCSB_buttonDown{bottom:0;background-position:0 -20px}.mCSB_horizontal.mCSB_inside>.mCSB_container{margin-right:0;margin-bottom:30px}.mCSB_horizontal.mCSB_outside>.mCSB_container{min-height:100%}.mCSB_horizontal>.mCSB_container.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal{width:auto;height:16px;top:auto;right:0;bottom:0;left:0}.mCustomScrollBox+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:-26px}.mCSB_scrollTools.mCSB_scrollTools_horizontal a+.mCSB_draggerContainer{margin:0 20px}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:2px;margin:7px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger{width:30px;height:100%;left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:12px;margin:2px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:8px;margin:4px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft,.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{display:block;position:absolute;width:20px;height:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft{left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{right:0}.mCSB_container_wrapper{position:absolute;height:auto;width:auto;overflow:hidden;top:0;left:0;right:0;bottom:0;margin-right:30px;margin-bottom:30px}.mCSB_container_wrapper>.mCSB_container{padding-right:30px;padding-bottom:30px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:20px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:20px}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden+.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:0}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:20px}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal{left:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container_wrapper{margin-right:0;margin-left:30px}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden>.mCSB_container{padding-right:0;box-sizing:border-box}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden>.mCSB_container{padding-bottom:0;box-sizing:border-box}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0;margin-left:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools,.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{transition:opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail{transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out}.mCS-autoHide>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:0;filter:"alpha(opacity=0)"}.mCustomScrollbar>.mCustomScrollBox>.mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollbar>.mCustomScrollBox ~ .mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollBox:hover>.mCSB_scrollTools,.mCustomScrollBox:hover ~ .mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:1;filter:"alpha(opacity=100)"}.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85);filter:"alpha(opacity=85)"}.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9);filter:"alpha(opacity=90)"}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACQCAYAAACPtWCAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxMURDMzE5NzIzQkNFMTExOTY0QkYwNzFDNzkwNTlDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOTMwRUZENEMxMUUxMUUzOUYxQkJGN0E1MDMzNTg1MCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOTMwRUZEM0MxMUUxMUUzOUYxQkJGN0E1MDMzNTg1MCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MGJlMjMyZC1hNzgzLTI1NGQtOTI4Yy02NDI0YmQxNTg0YWEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MTFEQzMxOTcyM0JDRTExMTk2NEJGMDcxQzc5MDU5QzQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5ZvSKsAAAH5ElEQVR42uyd3Y3jNhRGpZSQ7SJA3lyACtg+psEUoAL2zZjtIgtMBYqcWBsvh5e8/Cel8wECPLZ5JZMHpHj5UTNv2zYh1EozACIARACIUI8AHh/Oja6v9fnTKnee/73+vY7nK54/FUDzg7kRfNOIEB6N//PHVIag9flTAZSonBvBNxSEZuPXhqD1+VMB9N0UzhEgzRngq3X+Io0fA0HMENr6/KkAbkZDb5bX2gaNGUK1M6JS588G36PRXhvQ/KzEEOqDr/T5cwI4K//OPYTmArDJEG72GL6/cw+huQCsOYSXmAXbesotAoLYHiznEF59FmrrKUN7z5QeLOcQ3gJAF2ijDeHVAXSBNtoQ3hrAOUO8ywIofT8k3lUBLJmKGS4Z3TIRPHoeEAABEAABEAARAkAEgAgBIAJAhAAQASBCAIjODyCJ6JTKJRENgAAIgAAIgNUAxI6VACB2rHwA2spgSA0AAENq/BCMJT9hCL66JZ9NSRmBY1NSPIBHY7EtM/LGn22Z8UMwG9Mzzj5TepErbkxvOoR1dP4iEPJojrBJCA8nyggBDyeqNwsuASGPZxvw/DkARAgAEQAiBIAIABECQASACFUDsGQuLkfsrq+vZC4uR+zW16dNRJdo4Byxu76+GBtVzdg9XJ92Ka53+HJfY3LskeDLfY0hsTVmhBHgc9ZHhtjzmeFzVkbgOWyxQwEMnZXMHcOXcn1RMUIaN7SBa8OXcn0ACIDDAsgQzBDcdAhmEsIkpPkkhDQMaZjmaRizIUhEJzQEieh4ABEqJgBEAIgAECEARACIULcAnj5lUvL6zpAyKREjBsCUBrpsYvssiePcMUKH4J4bOcfSXNHHgfQMYY6luagnekXcA7aGsGsjQu8Q9mREkAAsaXfKAeFl7Vg5ILy6HxAAAZAhmCG47yGYSQiTkC4nIaRhSMM0TcOQiM7QwCSi0+8BEcoHPgAiAEQAiBAAIgBEqDWAt/34uh/Lfvz5fO++H+t+/LUf3zwxU8uPXaHz7Pz9e31/K1l+OD0AfDne9uN9k/X+/M4kHKnlhz52ve3H+/RfPtF2PD57K1V+yDoz4PnY/PoQIEotfwb4PhzwHMeHDaLU8qMDeBN6ruNzW092e/n85un5fOVHh+9m67lePrf1ZDdfeU9PeIr6++05Ej/uOf4wb0eE19Pzu19f/raVP8rZlmXM8uIdwhRuv2qhT7//dTnKsjSlqr9HOWFZS1V/j6WxUPtVbR0ALg74pPcW4bUGYKmMCZ/tdY9aJPgc7znrzwOwt/5ewesZwmMW/Pd+/O6AxQbGj/348nz9Wt4V47UiXsu7vjcp4rae+f78/V7/2/8w/Ni/+8Us74phgPSzvOd7ky9uDz1gzh5odgyhmgrYHEP4MEnL2B7oFRJzCFX564zvm/F6BfCuuO8y378Lr7UA3wNA7v3/Bd99912W9531pwD4rgW55/8XfAC4RpRdhddagFcHeHPA+z0oW/0FALxK4En3oD0PwY8Viu8B5b4/y0yZyo8u6i/DSgiJaBLRTVdCWIpjKa76gRkh/+wXM0JEHhAhAEQAiBAAIgBECADRNQEkDZNSoaRhwkQimkQ0e0JYiksuz54Q9oSwJyTznhCXtHtCtOVHF/UXKWlPiEaL8Frr55POqfUT9qRs9Rfg51uESYzWT9jVLNi2J2RzvH5I2hNis9Db3pP2hNgs/CWfrJpj5vtpT8ijsaXXx++37QmxWegFW711T4jNwl/yyaq5esBJ6LFiGj11T0fqnpJesgtRlvjUPR2pe0paAXhXQqLd07B5ytrKTB7QeobvroFEuydEGkK19afcFtoVgGsEQKvw2vZ9W7w1oCfsvedbIwBy1p9iU9JaogduBaBtT4ILIO2eBmnioN3TME9jDLuffr8HIFX9OSYOqvrrdSOStBJCIppENHtCWIpjTwhmhLSUDGaEiDwgQgCIABAhAEQAWGIycanJSe7JxGknJ5XSKZdKz+ROp5w5PTNVSChfKkGdO6F89gS1+UaqM1obT4qZ6pQ2Yw/ljNbGc8RMckqbsWs6og/5npZvyufMdT09Pyae826igzsa59Pyc9WfI2Z0/bUyq5oALg5QpAtcHPEXD3xbYDwNfC0X3xcJFEcDB9WfIuaSAl9t84I5C059Wr4p19PzpQZxxesZvuSn5bviBQAtxusNPlsPiFBTAF3OaPNG2FZmcsTT9lb3wN/Q079xEJ3Rh7fP87R8Z10oe6ug+mv9bxxMANcIYFZH/DViyFxjRr9OIFwjgAmqP0XM4PprCaEJoM8ZbcrnzHU5pWPiaSFsJaczOlf9OWJG118z5zSJaBLRva2EsBTHUlxTR/QhzAhpKRnMCBF5QIQAEAEgQzBDcLNZMJMQJiH4AUnD4Acs5QeUYsX6AZt6AGv7AR2xovyAUwMPYKgf0LXaEOsHlFYuTucH9Kw2RNefsHJxOj+grXFnZRnpM99a8JKwBNcawsXXuBZwgupPsRYcVH+tzQi1/YAacEL9gFqwa8x8i/oBleAE+QEDwK7SAyLUdAg2/YA2/5/Z24T4ATXD5j3id/Tiiv7FDyj4/6ZYP6By2Ayuv5auaJ8fUAPM6oi/RtyzrYPCJ167B5ig+lPcs62jwGcDUPLvScDE+gEl+E7nB/TMNKPrL+XJqQGz6iYrISSiSUTjB2QpDj8gZoS0lAxmhIg8IEJV9Y8AAwCuz3H3j+GlGwAAAABJRU5ErkJggg==');background-repeat:no-repeat;opacity:0.4;filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_buttonUp{background-position:0 0}.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -40px}.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -56px}.mCSB_scrollTools .mCSB_buttonUp:hover,.mCSB_scrollTools .mCSB_buttonDown:hover,.mCSB_scrollTools .mCSB_buttonLeft:hover,.mCSB_scrollTools .mCSB_buttonRight:hover{opacity:0.75;filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_buttonUp:active,.mCSB_scrollTools .mCSB_buttonDown:active,.mCSB_scrollTools .mCSB_buttonLeft:active,.mCSB_scrollTools .mCSB_buttonRight:active{opacity:0.9;filter:"alpha(opacity=90)"}.mCS-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:rgba(0,0,0,0.85)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:rgba(0,0,0,0.9)}.mCS-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-light-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);border-radius:1px}.mCS-light-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.75);border-radius:1px}.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px 0}.mCS-light-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -20px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -40px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px 0}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -20px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -40px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -56px}.mCS-light-thick.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);border-radius:2px}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;background-color:#fff;background-color:rgba(255,255,255,0.75);border-radius:2px}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px 0}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:6px;margin:5px auto}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px 0}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -20px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -40px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px 0}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -20px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -40px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -56px}.mCS-light-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.1)}.mCS-light-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:2px}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:2px;margin:7px auto}.mCS-dark-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-rounded.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.15)}.mCS-rounded.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger{height:14px}.mCS-rounded.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:14px;margin:0 1px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:14px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:14px;margin:1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:16px;height:16px;margin:-1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:4px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:16px;width:16px;margin:0 -1px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:4px;margin:6px 0}.mCS-rounded.mCSB_scrollTools .mCSB_buttonUp{background-position:0 -72px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -92px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -112px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -128px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px -72px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -92px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -112px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -128px}.mCS-rounded-dots.mCSB_scrollTools_vertical .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_vertical .mCSB_draggerRail{width:4px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{background-color:transparent;background-position:center}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQYV2NkIAAYiVbw//9/Y6DiM1ANJoyMjGdBbLgJQAX/kU0DKgDLkaQAvxW4HEvQFwCRcxIJK1XznAAAAABJRU5ErkJggg==");background-repeat:repeat-y;opacity:0.3;filter:"alpha(opacity=30)"}.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{height:4px;margin:6px 0;background-repeat:repeat-x}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px -72px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -92px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -112px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALElEQVQYV2NkIAAYSVFgDFR8BqrBBEifBbGRTfiPZhpYjiQFBK3A6l6CvgAAE9kGCd1mvgEAAAAASUVORK5CYII=")}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px -72px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -92px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -112px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -128px}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-y;background-image:linear-gradient(to right, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-x;background-image:linear-gradient(to bottom, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:70px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:70px}.mCS-3d.mCSB_scrollTools,.mCS-3d-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)"}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{border-radius:16px}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{width:8px;background-color:#000;background-color:rgba(0,0,0,0.2);box-shadow:inset 1px 0 1px rgba(0,0,0,0.5),inset -1px 0 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:8px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:8px;margin:4px 0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.5),inset 0 -1px 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:8px;margin:4px auto}.mCS-3d.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);box-shadow:inset 1px 0 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)"}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{border-radius:7px}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{border-radius:5px;box-shadow:inset 1px 0 0 rgba(255,255,255,0.4);width:12px;margin:2px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCSB_inside+.mCS-3d-thick.mCSB_scrollTools_vertical,.mCSB_inside+.mCS-3d-thick-dark.mCSB_scrollTools_vertical{right:1px}.mCS-3d-thick.mCSB_scrollTools_vertical,.mCS-3d-thick-dark.mCSB_scrollTools_vertical{box-shadow:inset 1px 0 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{bottom:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4);height:12px;width:auto}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{background-color:#000;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-thick-dark.mCSB_scrollTools{box-shadow:inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4),inset -1px 0 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4),inset 0 -1px 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#777}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{background-color:#fff;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{right:0;margin:12px 0}.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:0;margin:0 12px}.mCS-dir-rtl>.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCS-dir-rtl>.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{left:0;right:auto}.mCS-minimal.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-minimal.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:50px}.mCS-minimal.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:50px}.mCS-minimal.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.2);filter:"alpha(opacity=20)"}.mCS-minimal.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.5);filter:"alpha(opacity=50)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.2);filter:"alpha(opacity=20)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.5);filter:"alpha(opacity=50)"}.mCS-light-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{width:6px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-light-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px}.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:6px;margin:5px 0}.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:12px}.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:12px;margin:2px 0}.mCS-light-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{width:12px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-inset.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;margin:3px 5px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:6px;margin:5px 3px;position:absolute;width:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:12px;margin:2px 0}.mCS-inset.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-inset.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-inset.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-inset.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent;border-width:1px;border-style:solid;border-color:#fff;border-color:rgba(255,255,255,0.2);box-sizing:border-box}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{border-color:#000;border-color:rgba(0,0,0,0.2)}.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.6)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.6)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #3c3f41}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #3c3f41}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("../img/data-tables/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("../img/data-tables/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("../img/data-tables/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("../img/data-tables/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("../img/data-tables/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#212121}table.dataTable tbody tr.selected{background-color:#1b2326}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #3c3f41}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#2b2b2b}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#1b2326}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#b26d0a;color:#2b2b2b}table.dataTable.hover tbody tr:hover a,table.dataTable.display tbody tr:hover a{color:#2b2b2b !important}table.dataTable.hover tbody tr:hover td>.fa-plus,table.dataTable.display tbody tr:hover td>.fa-plus{color:#2b2b2b !important}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#925909}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#2b2b2b}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#212C30}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#1b2326}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#212C30}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#1b2326}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#925909}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#885308}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #3c3f41}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th:not(.sorting_disabled),table.dataTable.compact thead td:not(.sorting_disabled){padding:4px 17px 4px 4px}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 4px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#6f7477 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#adadad !important;border:1px solid transparent;background-color:#313335;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #313335), color-stop(100%, #2b2b2b));background:-webkit-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-moz-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-ms-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-o-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:linear-gradient(to bottom, #313335 0%,#2b2b2b 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#4f5355 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:#313335 !important;border:1px solid transparent;background-color:#c2760c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #c2760c), color-stop(100%, #c2760c));background:-webkit-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-moz-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-ms-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-o-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:linear-gradient(to bottom, #c2760c 0%,#c2760c 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#c2760c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #c2760c), color-stop(100%, #c2760c));background:-webkit-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-moz-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-ms-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-o-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:linear-gradient(to bottom, #c2760c 0%,#c2760c 100%)}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#63676a}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px solid #111;box-shadow:3px 3px 8px rgba(0,0,0,0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info h2{padding:0.5em;margin:0;font-weight:normal;border-bottom:1px solid #ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}ul.dt-button-collection.dropdown-menu{display:block;z-index:2002;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}ul.dt-button-collection.dropdown-menu.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}ul.dt-button-collection.dropdown-menu.fixed.two-column{margin-left:-150px}ul.dt-button-collection.dropdown-menu.fixed.three-column{margin-left:-225px}ul.dt-button-collection.dropdown-menu.fixed.four-column{margin-left:-300px}ul.dt-button-collection.dropdown-menu>*{-webkit-column-break-inside:avoid;break-inside:avoid}ul.dt-button-collection.dropdown-menu.two-column{width:300px;padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}ul.dt-button-collection.dropdown-menu.three-column{width:450px;padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}ul.dt-button-collection.dropdown-menu.four-column{width:600px;padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:2001}@media screen and (max-width: 767px){div.dt-buttons{float:none;width:100%;text-align:center;margin-bottom:0.5em}div.dt-buttons a.btn{float:none}}/*! + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.pf-landing .pf-landing-list li>i{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw,.pf-landing .pf-landing-list li>i,.modal-content h2[data-toggle="collapse"]:after,.modal-content h4[data-toggle="collapse"]:after{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li,.pf-landing .pf-landing-list li>i{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg,.pf-landing .pf-landing-list li>i.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.pf-landing .pf-landing-list li>i.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.pf-landing .pf-landing-list li>i.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pf-landing .pf-landing-list li>i.pull-left{margin-right:.3em}.fa.pull-right,.pf-landing .pf-landing-list li>i.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180,#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper.pf-character-active:after{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root #pf-landing-login .pf-dynamic-area .pf-character-image-wrapper.pf-character-active:after,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before,.pf-landing .pf-landing-list li>i:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.mCustomScrollbar{touch-action:none}.mCustomScrollbar.mCS_no_scrollbar{touch-action:auto}.mCustomScrollBox{position:relative;overflow:hidden;height:100%;max-width:100%;outline:none;direction:ltr}.mCSB_container{overflow:hidden;width:auto;height:auto}.mCSB_inside>.mCSB_container{margin-right:30px}.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container{margin-right:0;margin-left:30px}.mCS-dir-rtl>.mCSB_inside>.mCSB_container.mCS_no_scrollbar_y.mCS_y_hidden{margin-left:0}.mCSB_scrollTools{position:absolute;width:16px;height:auto;left:auto;top:0;right:0;bottom:0;opacity:0.75;filter:"alpha(opacity=75)"}.mCSB_outside+.mCSB_scrollTools{right:-26px}.mCS-dir-rtl>.mCSB_inside>.mCSB_scrollTools,.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{right:auto;left:0}.mCS-dir-rtl>.mCSB_outside+.mCSB_scrollTools{left:-26px}.mCSB_scrollTools .mCSB_draggerContainer{position:absolute;top:0;left:0;bottom:0;right:0;height:auto}.mCSB_scrollTools a+.mCSB_draggerContainer{margin:20px 0}.mCSB_scrollTools .mCSB_draggerRail{width:2px;height:100%;margin:0 auto;border-radius:16px;background-color:#000;background-color:rgba(0,0,0,0.4);filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_dragger{cursor:pointer;width:100%;height:30px;z-index:1}.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{position:relative;width:4px;height:100%;margin:0 auto;border-radius:16px;text-align:center;background-color:#fff;background-color:rgba(255,255,255,0.75);filter:"alpha(opacity=75)"}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:12px}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:8px}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown{display:block;position:absolute;height:20px;width:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools .mCSB_buttonDown{bottom:0;background-position:0 -20px}.mCSB_horizontal.mCSB_inside>.mCSB_container{margin-right:0;margin-bottom:30px}.mCSB_horizontal.mCSB_outside>.mCSB_container{min-height:100%}.mCSB_horizontal>.mCSB_container.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal{width:auto;height:16px;top:auto;right:0;bottom:0;left:0}.mCustomScrollBox+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:-26px}.mCSB_scrollTools.mCSB_scrollTools_horizontal a+.mCSB_draggerContainer{margin:0 20px}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:2px;margin:7px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger{width:30px;height:100%;left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:12px;margin:2px auto}.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:8px;margin:4px 0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft,.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{display:block;position:absolute;width:20px;height:100%;overflow:hidden;margin:0 auto;cursor:pointer}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonLeft{left:0}.mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_buttonRight{right:0}.mCSB_container_wrapper{position:absolute;height:auto;width:auto;overflow:hidden;top:0;left:0;right:0;bottom:0;margin-right:30px;margin-bottom:30px}.mCSB_container_wrapper>.mCSB_container{padding-right:30px;padding-bottom:30px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:20px}.mCSB_vertical_horizontal>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:20px}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden+.mCSB_scrollTools.mCSB_scrollTools_vertical{bottom:0}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{right:0}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_scrollTools.mCSB_scrollTools_horizontal{left:20px}.mCS-dir-rtl>.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden+.mCSB_scrollTools ~ .mCSB_scrollTools.mCSB_scrollTools_horizontal{left:0}.mCS-dir-rtl>.mCSB_inside>.mCSB_container_wrapper{margin-right:0;margin-left:30px}.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden>.mCSB_container{padding-right:0;box-sizing:border-box}.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden>.mCSB_container{padding-bottom:0;box-sizing:border-box}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_y.mCS_y_hidden{margin-right:0;margin-left:0}.mCustomScrollBox.mCSB_vertical_horizontal.mCSB_inside>.mCSB_container_wrapper.mCS_no_scrollbar_x.mCS_x_hidden{margin-bottom:0}.mCSB_scrollTools,.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{transition:opacity .2s ease-in-out, background-color .2s ease-in-out}.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger_bar,.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerRail{transition:width .2s ease-out .2s, height .2s ease-out .2s, margin-left .2s ease-out .2s, margin-right .2s ease-out .2s, margin-top .2s ease-out .2s, margin-bottom .2s ease-out .2s, opacity .2s ease-in-out, background-color .2s ease-in-out}.mCS-autoHide>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:0;filter:"alpha(opacity=0)"}.mCustomScrollbar>.mCustomScrollBox>.mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollbar>.mCustomScrollBox ~ .mCSB_scrollTools.mCSB_scrollTools_onDrag,.mCustomScrollBox:hover>.mCSB_scrollTools,.mCustomScrollBox:hover ~ .mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox>.mCSB_scrollTools,.mCS-autoHide:hover>.mCustomScrollBox ~ .mCSB_scrollTools{opacity:1;filter:"alpha(opacity=100)"}.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85);filter:"alpha(opacity=85)"}.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9);filter:"alpha(opacity=90)"}.mCSB_scrollTools .mCSB_buttonUp,.mCSB_scrollTools .mCSB_buttonDown,.mCSB_scrollTools .mCSB_buttonLeft,.mCSB_scrollTools .mCSB_buttonRight{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACQCAYAAACPtWCAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxMURDMzE5NzIzQkNFMTExOTY0QkYwNzFDNzkwNTlDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOTMwRUZENEMxMUUxMUUzOUYxQkJGN0E1MDMzNTg1MCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOTMwRUZEM0MxMUUxMUUzOUYxQkJGN0E1MDMzNTg1MCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MGJlMjMyZC1hNzgzLTI1NGQtOTI4Yy02NDI0YmQxNTg0YWEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MTFEQzMxOTcyM0JDRTExMTk2NEJGMDcxQzc5MDU5QzQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5ZvSKsAAAH5ElEQVR42uyd3Y3jNhRGpZSQ7SJA3lyACtg+psEUoAL2zZjtIgtMBYqcWBsvh5e8/Cel8wECPLZ5JZMHpHj5UTNv2zYh1EozACIARACIUI8AHh/Oja6v9fnTKnee/73+vY7nK54/FUDzg7kRfNOIEB6N//PHVIag9flTAZSonBvBNxSEZuPXhqD1+VMB9N0UzhEgzRngq3X+Io0fA0HMENr6/KkAbkZDb5bX2gaNGUK1M6JS588G36PRXhvQ/KzEEOqDr/T5cwI4K//OPYTmArDJEG72GL6/cw+huQCsOYSXmAXbesotAoLYHiznEF59FmrrKUN7z5QeLOcQ3gJAF2ijDeHVAXSBNtoQ3hrAOUO8ywIofT8k3lUBLJmKGS4Z3TIRPHoeEAABEAABEAARAkAEgAgBIAJAhAAQASBCAIjODyCJ6JTKJRENgAAIgAAIgNUAxI6VACB2rHwA2spgSA0AAENq/BCMJT9hCL66JZ9NSRmBY1NSPIBHY7EtM/LGn22Z8UMwG9Mzzj5TepErbkxvOoR1dP4iEPJojrBJCA8nyggBDyeqNwsuASGPZxvw/DkARAgAEQAiBIAIABECQASACFUDsGQuLkfsrq+vZC4uR+zW16dNRJdo4Byxu76+GBtVzdg9XJ92Ka53+HJfY3LskeDLfY0hsTVmhBHgc9ZHhtjzmeFzVkbgOWyxQwEMnZXMHcOXcn1RMUIaN7SBa8OXcn0ACIDDAsgQzBDcdAhmEsIkpPkkhDQMaZjmaRizIUhEJzQEieh4ABEqJgBEAIgAECEARACIULcAnj5lUvL6zpAyKREjBsCUBrpsYvssiePcMUKH4J4bOcfSXNHHgfQMYY6luagnekXcA7aGsGsjQu8Q9mREkAAsaXfKAeFl7Vg5ILy6HxAAAZAhmCG47yGYSQiTkC4nIaRhSMM0TcOQiM7QwCSi0+8BEcoHPgAiAEQAiBAAIgBEqDWAt/34uh/Lfvz5fO++H+t+/LUf3zwxU8uPXaHz7Pz9e31/K1l+OD0AfDne9uN9k/X+/M4kHKnlhz52ve3H+/RfPtF2PD57K1V+yDoz4PnY/PoQIEotfwb4PhzwHMeHDaLU8qMDeBN6ruNzW092e/n85un5fOVHh+9m67lePrf1ZDdfeU9PeIr6++05Ej/uOf4wb0eE19Pzu19f/raVP8rZlmXM8uIdwhRuv2qhT7//dTnKsjSlqr9HOWFZS1V/j6WxUPtVbR0ALg74pPcW4bUGYKmMCZ/tdY9aJPgc7znrzwOwt/5ewesZwmMW/Pd+/O6AxQbGj/348nz9Wt4V47UiXsu7vjcp4rae+f78/V7/2/8w/Ni/+8Us74phgPSzvOd7ky9uDz1gzh5odgyhmgrYHEP4MEnL2B7oFRJzCFX564zvm/F6BfCuuO8y378Lr7UA3wNA7v3/Bd99912W9531pwD4rgW55/8XfAC4RpRdhddagFcHeHPA+z0oW/0FALxK4En3oD0PwY8Viu8B5b4/y0yZyo8u6i/DSgiJaBLRTVdCWIpjKa76gRkh/+wXM0JEHhAhAEQAiBAAIgBECADRNQEkDZNSoaRhwkQimkQ0e0JYiksuz54Q9oSwJyTznhCXtHtCtOVHF/UXKWlPiEaL8Frr55POqfUT9qRs9Rfg51uESYzWT9jVLNi2J2RzvH5I2hNis9Db3pP2hNgs/CWfrJpj5vtpT8ijsaXXx++37QmxWegFW711T4jNwl/yyaq5esBJ6LFiGj11T0fqnpJesgtRlvjUPR2pe0paAXhXQqLd07B5ytrKTB7QeobvroFEuydEGkK19afcFtoVgGsEQKvw2vZ9W7w1oCfsvedbIwBy1p9iU9JaogduBaBtT4ILIO2eBmnioN3TME9jDLuffr8HIFX9OSYOqvrrdSOStBJCIppENHtCWIpjTwhmhLSUDGaEiDwgQgCIABAhAEQAWGIycanJSe7JxGknJ5XSKZdKz+ROp5w5PTNVSChfKkGdO6F89gS1+UaqM1obT4qZ6pQ2Yw/ljNbGc8RMckqbsWs6og/5npZvyufMdT09Pyae826igzsa59Pyc9WfI2Z0/bUyq5oALg5QpAtcHPEXD3xbYDwNfC0X3xcJFEcDB9WfIuaSAl9t84I5C059Wr4p19PzpQZxxesZvuSn5bviBQAtxusNPlsPiFBTAF3OaPNG2FZmcsTT9lb3wN/Q079xEJ3Rh7fP87R8Z10oe6ug+mv9bxxMANcIYFZH/DViyFxjRr9OIFwjgAmqP0XM4PprCaEJoM8ZbcrnzHU5pWPiaSFsJaczOlf9OWJG118z5zSJaBLRva2EsBTHUlxTR/QhzAhpKRnMCBF5QIQAEAEgQzBDcLNZMJMQJiH4AUnD4Acs5QeUYsX6AZt6AGv7AR2xovyAUwMPYKgf0LXaEOsHlFYuTucH9Kw2RNefsHJxOj+grXFnZRnpM99a8JKwBNcawsXXuBZwgupPsRYcVH+tzQi1/YAacEL9gFqwa8x8i/oBleAE+QEDwK7SAyLUdAg2/YA2/5/Z24T4ATXD5j3id/Tiiv7FDyj4/6ZYP6By2Ayuv5auaJ8fUAPM6oi/RtyzrYPCJ167B5ig+lPcs62jwGcDUPLvScDE+gEl+E7nB/TMNKPrL+XJqQGz6iYrISSiSUTjB2QpDj8gZoS0lAxmhIg8IEJV9Y8AAwCuz3H3j+GlGwAAAABJRU5ErkJggg==');background-repeat:no-repeat;opacity:0.4;filter:"alpha(opacity=40)"}.mCSB_scrollTools .mCSB_buttonUp{background-position:0 0}.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -40px}.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -56px}.mCSB_scrollTools .mCSB_buttonUp:hover,.mCSB_scrollTools .mCSB_buttonDown:hover,.mCSB_scrollTools .mCSB_buttonLeft:hover,.mCSB_scrollTools .mCSB_buttonRight:hover{opacity:0.75;filter:"alpha(opacity=75)"}.mCSB_scrollTools .mCSB_buttonUp:active,.mCSB_scrollTools .mCSB_buttonDown:active,.mCSB_scrollTools .mCSB_buttonLeft:active,.mCSB_scrollTools .mCSB_buttonRight:active{opacity:0.9;filter:"alpha(opacity=90)"}.mCS-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:rgba(0,0,0,0.85)}.mCS-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:rgba(0,0,0,0.9)}.mCS-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-light-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);border-radius:1px}.mCS-light-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.75);border-radius:1px}.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-light-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:4px;margin:6px auto}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px 0}.mCS-light-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -20px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -40px}.mCS-light-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -56px}.mCS-dark-2.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);border-radius:1px}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-2.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-2.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px 0}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -20px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -40px}.mCS-dark-2.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -56px}.mCS-light-thick.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{width:4px;background-color:#fff;background-color:rgba(255,255,255,0.1);border-radius:2px}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;background-color:#fff;background-color:rgba(255,255,255,0.75);border-radius:2px}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:4px;margin:6px 0}.mCS-light-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:6px;margin:5px auto}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-light-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-light-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px 0}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -20px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -40px}.mCS-light-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -56px}.mCS-dark-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75);border-radius:2px}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px 0}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -20px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -40px}.mCS-dark-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -56px}.mCS-light-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.1)}.mCS-light-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:2px}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%}.mCS-light-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:2px;margin:7px auto}.mCS-dark-thin.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-thin.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px 0}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -20px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -40px}.mCS-dark-thin.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -56px}.mCS-rounded.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.15)}.mCS-rounded.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger{height:14px}.mCS-rounded.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:14px;margin:0 1px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:14px}.mCS-rounded.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:14px;margin:1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{width:16px;height:16px;margin:-1px 0}.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:4px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_dragger .mCSB_dragger_bar{height:16px;width:16px;margin:0 -1px}.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-rounded-dark.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:4px;margin:6px 0}.mCS-rounded.mCSB_scrollTools .mCSB_buttonUp{background-position:0 -72px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonDown{background-position:0 -92px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonLeft{background-position:0 -112px}.mCS-rounded.mCSB_scrollTools .mCSB_buttonRight{background-position:0 -128px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.15)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-80px -72px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-80px -92px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-80px -112px}.mCS-rounded-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-80px -128px}.mCS-rounded-dots.mCSB_scrollTools_vertical .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_vertical .mCSB_draggerRail{width:4px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{background-color:transparent;background-position:center}.mCS-rounded-dots.mCSB_scrollTools .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQYV2NkIAAYiVbw//9/Y6DiM1ANJoyMjGdBbLgJQAX/kU0DKgDLkaQAvxW4HEvQFwCRcxIJK1XznAAAAABJRU5ErkJggg==");background-repeat:repeat-y;opacity:0.3;filter:"alpha(opacity=30)"}.mCS-rounded-dots.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-rounded-dots-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{height:4px;margin:6px 0;background-repeat:repeat-x}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonUp{background-position:-16px -72px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonDown{background-position:-16px -92px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonLeft{background-position:-20px -112px}.mCS-rounded-dots.mCSB_scrollTools .mCSB_buttonRight{background-position:-20px -128px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_draggerRail{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAALElEQVQYV2NkIAAYSVFgDFR8BqrBBEifBbGRTfiPZhpYjiQFBK3A6l6CvgAAE9kGCd1mvgEAAAAASUVORK5CYII=")}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-96px -72px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-96px -92px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-100px -112px}.mCS-rounded-dots-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-100px -128px}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-y;background-image:linear-gradient(to right, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{background-repeat:repeat-x;background-image:linear-gradient(to bottom, rgba(255,255,255,0.5) 0%,rgba(255,255,255,0) 100%)}.mCS-3d.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:70px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:70px}.mCS-3d.mCSB_scrollTools,.mCS-3d-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)"}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{border-radius:16px}.mCS-3d.mCSB_scrollTools .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{width:8px;background-color:#000;background-color:rgba(0,0,0,0.2);box-shadow:inset 1px 0 1px rgba(0,0,0,0.5),inset -1px 0 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:8px}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:8px;margin:4px 0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.5),inset 0 -1px 1px rgba(255,255,255,0.2)}.mCS-3d.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{width:100%;height:8px;margin:4px auto}.mCS-3d.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1);box-shadow:inset 1px 0 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1)}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools{opacity:1;filter:"alpha(opacity=30)"}.mCS-3d-thick.mCSB_scrollTools,.mCS-3d-thick-dark.mCSB_scrollTools,.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{border-radius:7px}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{border-radius:5px;box-shadow:inset 1px 0 0 rgba(255,255,255,0.4);width:12px;margin:2px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCSB_inside+.mCS-3d-thick.mCSB_scrollTools_vertical,.mCSB_inside+.mCS-3d-thick-dark.mCSB_scrollTools_vertical{right:1px}.mCS-3d-thick.mCSB_scrollTools_vertical,.mCS-3d-thick-dark.mCSB_scrollTools_vertical{box-shadow:inset 1px 0 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{bottom:1px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.5)}.mCS-3d-thick.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4);height:12px;width:auto}.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#555}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerContainer{background-color:#000;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-3d-thick.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-3d-thick-dark.mCSB_scrollTools{box-shadow:inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal{box-shadow:inset 0 1px 1px rgba(0,0,0,0.1),inset 0 0 14px rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 1px 0 0 rgba(255,255,255,0.4),inset -1px 0 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{box-shadow:inset 0 1px 0 rgba(255,255,255,0.4),inset 0 -1px 0 rgba(0,0,0,0.2)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#777}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerContainer{background-color:#fff;background-color:rgba(0,0,0,0.05);box-shadow:inset 1px 1px 16px rgba(0,0,0,0.1)}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-3d-thick-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{right:0;margin:12px 0}.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools.mCSB_scrollTools_horizontal,.mCustomScrollBox.mCS-minimal-dark+.mCSB_scrollTools+.mCSB_scrollTools.mCSB_scrollTools_horizontal{bottom:0;margin:0 12px}.mCS-dir-rtl>.mCSB_outside+.mCS-minimal.mCSB_scrollTools_vertical,.mCS-dir-rtl>.mCSB_outside+.mCS-minimal-dark.mCSB_scrollTools_vertical{left:0;right:auto}.mCS-minimal.mCSB_scrollTools .mCSB_draggerRail,.mCS-minimal-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent}.mCS-minimal.mCSB_scrollTools_vertical .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_vertical .mCSB_dragger{height:50px}.mCS-minimal.mCSB_scrollTools_horizontal .mCSB_dragger,.mCS-minimal-dark.mCSB_scrollTools_horizontal .mCSB_dragger{width:50px}.mCS-minimal.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.2);filter:"alpha(opacity=20)"}.mCS-minimal.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.5);filter:"alpha(opacity=50)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.2);filter:"alpha(opacity=20)"}.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-minimal-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.5);filter:"alpha(opacity=50)"}.mCS-light-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{width:6px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-light-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px}.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-light-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:6px;margin:5px 0}.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_vertical.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{width:12px}.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-light-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_dragger.mCSB_dragger_onDrag_expanded+.mCSB_draggerRail,.mCS-dark-3.mCSB_scrollTools_horizontal.mCSB_scrollTools_onDrag_expand .mCSB_draggerContainer:hover .mCSB_draggerRail{height:12px;margin:2px 0}.mCS-light-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-light-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-dark-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-dark-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-dark-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-dark-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{width:12px;background-color:#000;background-color:rgba(0,0,0,0.2)}.mCS-inset.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{width:6px;margin:3px 5px;position:absolute;height:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar{height:6px;margin:5px 3px;position:absolute;width:auto;top:0;bottom:0;left:0;right:0}.mCS-inset.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3.mCSB_scrollTools_horizontal .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools_horizontal .mCSB_draggerRail{width:100%;height:12px;margin:2px 0}.mCS-inset.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonUp{background-position:-32px -72px}.mCS-inset.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonDown{background-position:-32px -92px}.mCS-inset.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonLeft{background-position:-40px -112px}.mCS-inset.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3.mCSB_scrollTools .mCSB_buttonRight{background-position:-40px -128px}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.1)}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonUp,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonUp{background-position:-112px -72px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonDown,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonDown{background-position:-112px -92px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonLeft,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonLeft{background-position:-120px -112px}.mCS-inset-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_buttonRight,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_buttonRight{background-position:-120px -128px}.mCS-inset-2.mCSB_scrollTools .mCSB_draggerRail,.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:transparent;border-width:1px;border-style:solid;border-color:#fff;border-color:rgba(255,255,255,0.2);box-sizing:border-box}.mCS-inset-2-dark.mCSB_scrollTools .mCSB_draggerRail{border-color:#000;border-color:rgba(0,0,0,0.2)}.mCS-inset-3.mCSB_scrollTools .mCSB_draggerRail{background-color:#fff;background-color:rgba(255,255,255,0.6)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_draggerRail{background-color:#000;background-color:rgba(0,0,0,0.6)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.75)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.85)}.mCS-inset-3.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#000;background-color:rgba(0,0,0,0.9)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.75)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:hover .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.85)}.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger:active .mCSB_dragger_bar,.mCS-inset-3-dark.mCSB_scrollTools .mCSB_dragger.mCSB_dragger_onDrag .mCSB_dragger_bar{background-color:#fff;background-color:rgba(255,255,255,0.9)}table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #3c3f41}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #3c3f41}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("../img/data-tables/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("../img/data-tables/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("../img/data-tables/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("../img/data-tables/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("../img/data-tables/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#212121}table.dataTable tbody tr.selected{background-color:#1b2326}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #3c3f41}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#2b2b2b}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#1b2326}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#b26d0a;color:#2b2b2b}table.dataTable.hover tbody tr:hover a,table.dataTable.display tbody tr:hover a{color:#2b2b2b !important}table.dataTable.hover tbody tr:hover td>.fa-plus,table.dataTable.display tbody tr:hover td>.fa-plus{color:#2b2b2b !important}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#925909}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#2b2b2b}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#212C30}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#1b2326}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#212C30}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#1b2326}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#925909}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#885308}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #3c3f41}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th:not(.sorting_disabled),table.dataTable.compact thead td:not(.sorting_disabled){padding:4px 17px 4px 4px}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 4px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#6f7477 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#adadad !important;border:1px solid transparent;background-color:#313335;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #313335), color-stop(100%, #2b2b2b));background:-webkit-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-moz-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-ms-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:-o-linear-gradient(top, #313335 0%, #2b2b2b 100%);background:linear-gradient(to bottom, #313335 0%,#2b2b2b 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#4f5355 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:#313335 !important;border:1px solid transparent;background-color:#c2760c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #c2760c), color-stop(100%, #c2760c));background:-webkit-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-moz-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-ms-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-o-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:linear-gradient(to bottom, #c2760c 0%,#c2760c 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#c2760c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #c2760c), color-stop(100%, #c2760c));background:-webkit-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-moz-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-ms-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:-o-linear-gradient(top, #c2760c 0%, #c2760c 100%);background:linear-gradient(to bottom, #c2760c 0%,#c2760c 100%)}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#63676a}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px solid #111;box-shadow:3px 3px 8px rgba(0,0,0,0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info h2{padding:0.5em;margin:0;font-weight:normal;border-bottom:1px solid #ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}ul.dt-button-collection.dropdown-menu{display:block;z-index:2002;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}ul.dt-button-collection.dropdown-menu.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}ul.dt-button-collection.dropdown-menu.fixed.two-column{margin-left:-150px}ul.dt-button-collection.dropdown-menu.fixed.three-column{margin-left:-225px}ul.dt-button-collection.dropdown-menu.fixed.four-column{margin-left:-300px}ul.dt-button-collection.dropdown-menu>*{-webkit-column-break-inside:avoid;break-inside:avoid}ul.dt-button-collection.dropdown-menu.two-column{width:300px;padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}ul.dt-button-collection.dropdown-menu.three-column{width:450px;padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}ul.dt-button-collection.dropdown-menu.four-column{width:600px;padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:2001}@media screen and (max-width: 767px){div.dt-buttons{float:none;width:100%;text-align:center;margin-bottom:0.5em}div.dt-buttons a.btn{float:none}}/*! * DataTables + Font Awesome integration * License: MIT - http://datatables.net/license */table.dataTable thead th{position:relative;background-image:none !important}table.dataTable thead th.sorting:after,table.dataTable thead th.sorting_asc:after,table.dataTable thead th.sorting_desc:after{position:absolute;top:12px;right:8px;display:block;font-family:FontAwesome;margin-top:-8px}table.dataTable thead th.sorting:after{content:"\f0dc";color:#5cb85c;font-size:0.8em;margin-top:-8px}table.dataTable thead th.sorting_asc:after{content:"\f0de"}table.dataTable thead th.sorting_desc:after{content:"\f0dd"}div.dataTables_scrollBody table.dataTable thead th.sorting:after,div.dataTables_scrollBody table.dataTable thead th.sorting_asc:after,div.dataTables_scrollBody table.dataTable thead th.sorting_desc:after{content:""}div.dataTables_paginate a.paginate_button.first,div.dataTables_paginate a.paginate_button.previous{position:relative;padding-left:24px}div.dataTables_paginate a.paginate_button.next,div.dataTables_paginate a.paginate_button.last{position:relative;padding-right:24px}div.dataTables_paginate a.first:before,div.dataTables_paginate a.previous:before{position:absolute;top:7px;left:10px;display:block;font-family:FontAwesome}div.dataTables_paginate a.next:after,div.dataTables_paginate a.last:after{position:absolute;top:7px;right:10px;display:block;font-family:FontAwesome}div.dataTables_paginate a.first:before{content:"\f100"}div.dataTables_paginate a.previous:before{content:"\f104"}div.dataTables_paginate a.next:after{content:"\f105"}div.dataTables_paginate a.last:after{content:"\f101"}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty{cursor:default !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before{display:none !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child{position:relative;padding-left:30px;cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child:before{top:9px;left:4px;height:14px;width:14px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th:first-child:before{content:'-';background-color:#d33333}table.dataTable.dtr-inline.collapsed>tbody>tr.child td:before{display:none}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child{padding-left:27px}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child:before{top:5px;left:4px;height:14px;width:14px;border-radius:14px;line-height:14px;text-indent:3px}table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>th.control{position:relative;cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>th.control:before{top:50%;left:50%;height:16px;width:16px;margin-top:-10px;margin-left:-10px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before{content:'-';background-color:#d33333}table.dataTable>tbody>tr.child{padding:0.5em 1em}table.dataTable>tbody>tr.child:hover{background:transparent !important}table.dataTable>tbody>tr.child ul{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul li{border-bottom:1px solid #efefef;padding:0.5em 0}table.dataTable>tbody>tr.child ul li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul li:last-child{border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:bold}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:50%;overflow:auto;margin:auto;z-index:102;background-color:#f5f5f7;border:1px solid black;border-radius:0.5em;box-shadow:0 12px 30px rgba(0,0,0,0.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:1em}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;border:1px solid #eaeaea;background-color:#f9f9f9;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-close:hover{background-color:#eaeaea}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0,0,0,0.6)}@media screen and (max-width: 767px){div.dtr-modal div.dtr-modal-display{width:95%}}table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#1b2326}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#1b2326}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#925909}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#171e20}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#171e20}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#925909}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable td.select-checkbox{position:relative}table.dataTable td.select-checkbox:before,table.dataTable td.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable td.select-checkbox:before{content:' ';margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after{content:'\2714';margin-top:-11px;margin-left:-4px;text-align:center;text-shadow:1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}/*! X-editable - v1.5.0 @@ -38,4 +38,4 @@ * ======================================================================== * Copyright 2014 Min Hur, The New York Times Company * Licensed under MIT - * ======================================================================== */label.checkbox .toggle,label.checkbox.inline .toggle{margin-left:-20px;margin-right:5px}.toggle{min-width:40px;height:20px;position:relative;overflow:hidden}.toggle input[type="checkbox"]{display:none}.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left 0.35s;-webkit-transition:left 0.35s;-moz-user-select:none;-webkit-user-select:none}.toggle.off .toggle-group{left:-100%}.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}.toggle-handle{position:relative;margin:0 auto;padding-top:0px;padding-bottom:0px;height:100%;width:0px;border-width:0 1px}.toggle-handle.btn-mini{top:-2px}.toggle.btn{min-width:30px}.toggle-on.btn{padding-right:24px}.toggle-off.btn{padding-left:24px}.toggle.btn-large{min-width:40px}.toggle-on.btn-large{padding-right:35px}.toggle-off.btn-large{padding-left:35px}.toggle.btn-small{min-width:25px}.toggle-on.btn-small{padding-right:20px}.toggle-off.btn-small{padding-left:20px}.toggle.btn-mini{min-width:20px}.toggle-on.btn-mini{padding-right:12px}.toggle-off.btn-mini{padding-left:12px}.checkbox{padding-left:20px}.checkbox label{display:inline-block;vertical-align:middle;position:relative;padding-left:5px}.checkbox label::before{content:"";display:inline-block;position:absolute;width:17px;height:17px;left:0;margin-left:-20px;border:1px solid #63676a;border-radius:3px;background-color:#313335;-webkit-transition:border 0.15s ease-in-out,color 0.15s ease-in-out;transition:border 0.15s ease-in-out,color 0.15s ease-in-out}.checkbox label::after{display:inline-block;position:absolute;width:16px;height:16px;left:0;top:0;margin-left:-20px;padding-left:3px;padding-top:1px;font-size:11px;color:#adadad}.checkbox input[type="checkbox"],.checkbox input[type="radio"]{opacity:0;z-index:1}.checkbox input[type="checkbox"]:focus+label::before,.checkbox input[type="radio"]:focus+label::before{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.checkbox input[type="checkbox"]:checked+label::after,.checkbox input[type="radio"]:checked+label::after{font-family:"FontAwesome";content:""}.checkbox input[type="checkbox"]:indeterminate+label::after,.checkbox input[type="radio"]:indeterminate+label::after{display:block;content:"";width:10px;height:3px;background-color:#555555;border-radius:2px;margin-left:-16.5px;margin-top:7px}.checkbox input[type="checkbox"]:disabled+label,.checkbox input[type="radio"]:disabled+label{opacity:0.65}.checkbox input[type="checkbox"]:disabled+label::before,.checkbox input[type="radio"]:disabled+label::before{background-color:#adadad;cursor:not-allowed}.checkbox.checkbox-circle label::before{border-radius:50%}.checkbox.checkbox-inline{margin-top:0}.checkbox-primary input[type="checkbox"]:checked+label::before,.checkbox-primary input[type="radio"]:checked+label::before{background-color:#375959;border-color:#375959}.checkbox-primary input[type="checkbox"]:checked+label::after,.checkbox-primary input[type="radio"]:checked+label::after{color:#fff}.checkbox-danger input[type="checkbox"]:checked+label::before,.checkbox-danger input[type="radio"]:checked+label::before{background-color:#a52521;border-color:#a52521}.checkbox-danger input[type="checkbox"]:checked+label::after,.checkbox-danger input[type="radio"]:checked+label::after{color:#fff}.checkbox-info input[type="checkbox"]:checked+label::before,.checkbox-info input[type="radio"]:checked+label::before{background-color:#316490;border-color:#316490}.checkbox-info input[type="checkbox"]:checked+label::after,.checkbox-info input[type="radio"]:checked+label::after{color:#fff}.checkbox-warning input[type="checkbox"]:checked+label::before,.checkbox-warning input[type="radio"]:checked+label::before{background-color:#e28a0d;border-color:#e28a0d}.checkbox-warning input[type="checkbox"]:checked+label::after,.checkbox-warning input[type="radio"]:checked+label::after{color:#fff}.checkbox-success input[type="checkbox"]:checked+label::before,.checkbox-success input[type="radio"]:checked+label::before{background-color:#4f9e4f;border-color:#4f9e4f}.checkbox-success input[type="checkbox"]:checked+label::after,.checkbox-success input[type="radio"]:checked+label::after{color:#fff}.checkbox-primary input[type="checkbox"]:indeterminate+label::before,.checkbox-primary input[type="radio"]:indeterminate+label::before{background-color:#375959;border-color:#375959}.checkbox-primary input[type="checkbox"]:indeterminate+label::after,.checkbox-primary input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-danger input[type="checkbox"]:indeterminate+label::before,.checkbox-danger input[type="radio"]:indeterminate+label::before{background-color:#a52521;border-color:#a52521}.checkbox-danger input[type="checkbox"]:indeterminate+label::after,.checkbox-danger input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-info input[type="checkbox"]:indeterminate+label::before,.checkbox-info input[type="radio"]:indeterminate+label::before{background-color:#316490;border-color:#316490}.checkbox-info input[type="checkbox"]:indeterminate+label::after,.checkbox-info input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-warning input[type="checkbox"]:indeterminate+label::before,.checkbox-warning input[type="radio"]:indeterminate+label::before{background-color:#e28a0d;border-color:#e28a0d}.checkbox-warning input[type="checkbox"]:indeterminate+label::after,.checkbox-warning input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-success input[type="checkbox"]:indeterminate+label::before,.checkbox-success input[type="radio"]:indeterminate+label::before{background-color:#4f9e4f;border-color:#4f9e4f}.checkbox-success input[type="checkbox"]:indeterminate+label::after,.checkbox-success input[type="radio"]:indeterminate+label::after{background-color:#fff}.radio{padding-left:20px}.radio label{display:inline-block;vertical-align:middle;position:relative;padding-left:5px}.radio label::before{content:"";display:inline-block;position:absolute;width:17px;height:17px;left:0;margin-left:-20px;border:1px solid #63676a;border-radius:50%;background-color:#313335;-webkit-transition:border 0.15s ease-in-out;transition:border 0.15s ease-in-out}.radio label::after{display:inline-block;position:absolute;content:" ";width:11px;height:11px;left:3px;top:3px;margin-left:-20px;border-radius:50%;background-color:#adadad;-webkit-transform:scale(0,0);-ms-transform:scale(0,0);transform:scale(0,0);-webkit-transition:-webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);-moz-transition:-moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);-o-transition:-o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);transition:transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33)}.radio input[type="radio"]{opacity:0;z-index:1}.radio input[type="radio"]:focus+label::before{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.radio input[type="radio"]:checked+label::after{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1)}.radio input[type="radio"]:disabled+label{opacity:0.65}.radio input[type="radio"]:disabled+label::before{cursor:not-allowed}.radio.radio-inline{margin-top:0}.radio-primary input[type="radio"]+label::after{background-color:#375959}.radio-primary input[type="radio"]:checked+label::before{border-color:#375959}.radio-primary input[type="radio"]:checked+label::after{background-color:#375959}.radio-danger input[type="radio"]+label::after{background-color:#a52521}.radio-danger input[type="radio"]:checked+label::before{border-color:#a52521}.radio-danger input[type="radio"]:checked+label::after{background-color:#a52521}.radio-info input[type="radio"]+label::after{background-color:#316490}.radio-info input[type="radio"]:checked+label::before{border-color:#316490}.radio-info input[type="radio"]:checked+label::after{background-color:#316490}.radio-warning input[type="radio"]+label::after{background-color:#e28a0d}.radio-warning input[type="radio"]:checked+label::before{border-color:#e28a0d}.radio-warning input[type="radio"]:checked+label::after{background-color:#e28a0d}.radio-success input[type="radio"]+label::after{background-color:#4f9e4f}.radio-success input[type="radio"]:checked+label::before{border-color:#4f9e4f}.radio-success input[type="radio"]:checked+label::after{background-color:#4f9e4f}input[type="checkbox"].styled:checked+label:after,input[type="radio"].styled:checked+label:after{font-family:"FontAwesome";content:""}input[type="checkbox"] .styled:checked+label::before,input[type="radio"] .styled:checked+label::before{color:#fff}input[type="checkbox"] .styled:checked+label::after,input[type="radio"] .styled:checked+label::after{color:#fff}html{margin:0;padding:0;height:100%;position:relative}body{margin:0;padding:0;min-height:100%;direction:ltr}body.mobile-view-activated.hidden-menu{overflow-x:hidden}body.modal-open{overflow:hidden !important}a:hover,a:active,a:focus,button,button:active,button:focus,object,embed,input::-moz-focus-inner{outline:0}h1,h3,h4{margin:0;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.page-title{margin:12px 0 28px}.page-title span{font-size:15px;color:#313335;display:inline-block;vertical-align:1px}label{font-weight:normal}*:focus{outline:0 !important}a,input,button{-ms-touch-action:none !important}textarea:focus,select:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{outline:0;outline:thin dotted \9;box-shadow:inset -1px 1px 5px 0 rgba(0,0,0,0.8) !important}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn,.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn,.input-xs,.form-control{border-radius:0px !important;-webkit-border-radius:0px !important;-moz-border-radius:0px !important}.input-xs{height:24px;padding:2px 10px;font-size:11px;line-height:1.5}.btn-xs,.btn-group-xs>.btn{padding:0px 2px;font-size:10px;line-height:1.3}.btn-sm,.btn-group-sm>.btn{padding:5px 8px 4px}.btn-lg,.btn-group-lg>.btn{padding:10px 16px}.no-space{margin:0}.no-space>[class*="col-"]{margin:0 !important;padding-right:0;padding-left:0}h1{letter-spacing:-1px;font-size:22px;margin:10px 0}h1 small{font-size:12px;font-weight:300;letter-spacing:-1px}h2{font-size:20px;margin:20px 0;line-height:normal}h3{display:block;font-size:17px;font-weight:400;margin:20px 0;line-height:normal}h4{line-height:normal;margin:20px 0 10px 0}h5{font-size:14px;font-weight:300;margin-top:0;margin-bottom:10px;line-height:normal}h6{font-size:13px;margin:10px 0;font-weight:bold;line-height:normal}.row-seperator-header{margin:15px 14px 20px;border-bottom:none;display:block;color:#303133;font-size:20px;font-weight:400}.center-canvas,.center-child-canvas>canvas{display:block !important;margin:0 auto !important}.smart-accordion-default.panel-group{margin-bottom:0px}.smart-accordion-default.panel-group .panel+.panel{margin-top:-1px}.smart-accordion-default.panel-group .panel-heading{padding:0px}.smart-accordion-default.panel-group .panel-title a{display:block;padding:10px 15px;text-decoration:none !important}.smart-accordion-default .panel-heading,.panel-group .panel{border-radius:0px;-webkit-border-radius:0px;-moz-border-radius:0px}.smart-accordion-default .panel-default>.panel-heading{background-color:#f3f3f3}.smart-accordion-default .panel-default{border-color:#8d9194}.smart-accordion-default .panel-title>a>:first-child{display:none}.smart-accordion-default .panel-title>a.collapsed>.fa{display:none}.smart-accordion-default .panel-title>a.collapsed>:first-child{display:inline-block}.no-padding .smart-accordion-default>div{border-left:none !important;border-right:none !important}.no-padding .smart-accordion-default>div:first-child{border-top:none !important}.no-padding .smart-accordion-default>div:last-child{border-bottom:none !important}.onoffswitch-container{margin-top:4px;margin-left:7px;display:inline-block}.onoffswitch{position:relative;width:50px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;margin-top:3px;margin-bottom:3px;margin-left:5px;display:inline-block;vertical-align:middle}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:1px solid #484c4e;border-radius:50px;border-color:#777b7f #7c8184 #686c6f;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.onoffswitch-inner{width:200%;margin-left:-100%;display:block}.onoffswitch-inner:before,.onoffswitch-inner:after{float:left;width:50%;height:15px;padding:0;line-height:15px;font-size:10px;color:#fff;font-family:Trebuchet, Arial, sans-serif;font-weight:bold;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.onoffswitch-inner:before{content:attr(data-swchon-text);text-shadow:0 -1px 0 #313335;padding-left:7px;background-color:#3276b1;color:#fff;box-shadow:inset 0 2px 6px rgba(0,0,0,0.5),0 1px 2px rgba(0,0,0,0.05);text-align:left}.onoffswitch-inner:after{content:attr(data-swchoff-text);padding-right:7px;text-shadow:0 -1px 0 #fff;background-color:#fff;color:#3c3f41;text-align:right;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.onoffswitch-switch{width:19px;height:19px;margin:-2px;background:white;border:1px solid #64686b;border-radius:50px;position:absolute;top:0;bottom:0;right:32px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;background-color:#eaeaea;background-image:-moz-linear-gradient(top, #fff, #adadad);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#adadad));background-image:-webkit-linear-gradient(top, #fff, #adadad);background-image:-o-linear-gradient(top, #fff, #adadad);background-image:linear-gradient(to bottom, #ffffff,#adadad);background-repeat:repeat-x;-webkit-box-shadow:1px 1px 4px 0px rgba(0,0,0,0.3);box-shadow:1px 1px 4px 0px rgba(0,0,0,0.3)}.onoffswitch-checkbox+.onoffswitch-label .onoffswitch-switch:before,.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch:before{content:"\f00d";color:#a52521;display:block;text-align:center;line-height:19px;font-size:10px;text-shadow:0 -1px 0 #fff;font-weight:bold;font-family:FontAwesome}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch:before{content:"\f00c";color:#428bca}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-left:0;display:block}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:0px}.onoffswitch-switch:hover{background-color:#adadad}.onoffswitch-switch:active{background-color:#adadad;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.onoffswitch-checkbox:disabled+.onoffswitch-label .onoffswitch-inner:after,.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-inner:before{text-shadow:0 1px 0 #fff;background:#bfbfbf;color:#313335}.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-switch,.onoffswitch-checkbox:disabled+.onoffswitch-label .onoffswitch-switch{background-color:#eaeaea;background-image:-moz-linear-gradient(top, #bfbfbf, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#bfbfbf), to(#eaeaea));background-image:-webkit-linear-gradient(top, #bfbfbf, #eaeaea);background-image:-o-linear-gradient(top, #bfbfbf, #eaeaea);background-image:linear-gradient(to bottom, #bfbfbf,#eaeaea);box-shadow:none !important}.onoffswitch-checkbox:disabled+.onoffswitch-label,.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-label{border-color:#74797c #63676a #525558 !important}.onoffswitch-checkbox:checked+.onoffswitch-label{border-color:#3276b1 #2a6395 #255681}.onoffswitch+span,.onoffswitch-title{display:inline-block;vertical-align:middle;margin-top:-5px}.form-control{box-shadow:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important}.form hr{margin-left:-13px;margin-right:-13px;border-color:rgba(0,0,0,0.1);margin-top:20px;margin-bottom:20px}.form fieldset{display:block;border:none;background:rgba(255,255,255,0.9);position:relative}fieldset{position:relative}.form-actions{display:block;padding:13px 14px 15px;border-top:1px solid rgba(0,0,0,0.1);background:rgba(239,239,239,0.9);margin-top:25px;margin-left:-13px;margin-right:-13px;margin-bottom:-13px;text-align:right}.well .form-actions{margin-left:-19px;margin-right:-19px;margin-bottom:-19px}.well.well-lg .form-actions{margin-left:-24px;margin-right:-24px;margin-bottom:-24px}.well.well-sm .form-actions{margin-left:-9px;margin-right:-9px;margin-bottom:-9px}.popover-content .form-actions{margin:0 -14px -9px;border-radius:0 0 3px 3px;padding:9px 14px}.no-padding .form .form-actions{margin:0;display:block;padding:13px 14px 15px;border-top:1px solid rgba(0,0,0,0.1);background:rgba(248,248,248,0.9);text-align:right;margin-top:25px}.form header,legend{display:block;padding:8px 0;border-bottom:1px dashed rgba(0,0,0,0.2);background:#fff;font-size:16px;font-weight:300;color:#2b2b2b;margin:25px 0px 20px}.no-padding .form header{margin:25px 14px 0}.form header:first-child{margin-top:10px}legend{font-weight:400;margin-top:0px;background:none}.input-group-addon{padding:6px 10px;will-change:background-color, border-color;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;-webkit-transition:all ease-out 0.15s;transition:all ease-out 0.15s}.input-group-addon .fa,.input-group-addon .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .input-group-addon li>i{font-size:14px}.input-group-addon .fa-lg,.input-group-addon .fa-2x{font-size:2em}.input-group-addon .fa-3x,.input-group-addon .fa-4x,.input-group-addon .fa-5x{font-size:30px}input[type="text"]:focus+.input-group-addon,input[type="password"]:focus+.input-group-addon,input[type="email"]:focus+.input-group-addon{border-color:#568a89;color:#568a89}.has-warning input[type="text"],.has-warning input[type="text"]+.input-group-addon{border-color:#e28a0d}.has-warning input[type="text"]+.input-group-addon{background-color:#fbe3c0;color:#2b2b2b}.has-warning input[type="text"]:focus,.has-warning input[type="text"]:focus+.input-group-addon{border-color:#e28a0d}.has-warning input[type="text"]:focus+.input-group-addon{background-color:#e28a0d;color:#fff}.has-error .input-group-addon{border-color:#d9534f !important;background:#d9534f !important;color:#2b2b2b !important}.has-success .input-group-addon{border-color:#4f9e4f !important;background-color:#2b2b2b !important;color:#4f9e4f !important}.form fieldset .form-group:last-child,.form fieldset .form-group:last-child .note,.form .form-group:last-child,.form .form-group:last-child .note{margin-bottom:0}.note{margin-top:6px;padding:0 1px;font-size:11px;line-height:15px;color:#63676a}.input-icon-right{position:relative}.input-icon-right>i,.input-icon-left>i{position:absolute;right:10px;top:30%;font-size:16px;color:#bfbfbf}.input-icon-left>i{right:auto;left:24px}.input-icon-right .form-control{padding-right:27px}.input-icon-left .form-control{padding-left:29px}input[type="text"].ui-autocomplete-loading,input[type="password"].ui-autocomplete-loading,input[type="datetime"].ui-autocomplete-loading,input[type="datetime-local"].ui-autocomplete-loading,input[type="date"].ui-autocomplete-loading,input[type="month"].ui-autocomplete-loading,input[type="time"].ui-autocomplete-loading,input[type="week"].ui-autocomplete-loading,input[type="number"].ui-autocomplete-loading,input[type="email"].ui-autocomplete-loading,input[type="url"].ui-autocomplete-loading,input[type="search"].ui-autocomplete-loading,input[type="tel"].ui-autocomplete-loading,input[type="color"].ui-autocomplete-loading{background-image:url("../img/select2-spinner.gif") !important;background-repeat:no-repeat;background-position:99% 50%;padding-right:27px}.input-group-addon .checkbox,.input-group-addon .radio{min-height:0px;margin-right:0px !important;padding-top:0}.input-group-addon label input[type="checkbox"].checkbox+span,.input-group-addon label input[type="radio"].radiobox+span,.input-group-addon label input[type="radio"].radiobox+span:before,.input-group-addon label input[type="checkbox"].checkbox+span:before{margin-right:0px}.input-group-addon .onoffswitch,.input-group-addon .onoffswitch-label{margin:0}.alert{margin-bottom:10px;margin-top:0px;padding:5px 15px 5px 34px;color:#675100;border-width:0px;border-left-width:3px;padding:10px}.alert .ui-pnotify-title{line-height:12px}.alert .ui-pnotify-text{font-size:10px}.alert .close{top:0px;right:-5px;line-height:20px}.alert-heading{font-weight:600}.alert-danger{border-color:#a52521;color:#2b2b2b;background:#f6d1d0;text-shadow:none}.alert-danger .ui-pnotify-icon{color:#a52521}.alert-warning{border-color:#e28a0d;color:#2b2b2b;background:#fdedd8}.alert-warning .ui-pnotify-icon{color:#e28a0d}.alert-success{border-color:#4f9e4f;color:#2b2b2b;background:#d1e8d1}.alert-success .ui-pnotify-icon{color:#4f9e4f}.alert-info{border-color:#316490;color:#2b2b2b;background:#abc9e2}.alert-info .ui-pnotify-icon{color:#316490}.progress-micro{height:2px !important;line-height:2px !important}.progress-xs{height:7px !important;line-height:7px !important}.progress-sm{height:14px !important;line-height:14px !important}.progress-lg{height:30px !important;line-height:30px !important}.progress .progress-bar{position:absolute;overflow:hidden;line-height:18px}.progress .progressbar-back-text{position:absolute;width:100%;height:100%;font-size:12px;line-height:20px;text-align:center}.progress .progressbar-front-text{display:block;width:100%;font-size:12px;line-height:20px;text-align:center}.progress.right .progress-bar{right:0}.progress.right .progressbar-front-text{position:absolute;right:0}.progress.vertical{width:25px;height:100%;min-height:150px;margin-right:20px;display:inline-block;margin-bottom:0px}.progress.wide-bar{width:40px}.progress.vertical.bottom{position:relative}.progress.vertical.bottom .progressbar-front-text{position:absolute;bottom:0}.progress.vertical .progress-bar{width:100%;height:0;-webkit-transition:height 0.6s ease;transition:height 0.6s ease}.progress.vertical.bottom .progress-bar{position:absolute;bottom:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{position:relative;margin-bottom:20px;overflow:hidden;height:18px;background:#adadad;-webkit-box-shadow:0 1px 0 transparent,0 0 0 1px #aeb1b3 inset;box-shadow:0 1px 0 transparent,0 0 0 1px #aeb1b3 inset;-moz-border-radius:0px;-webkit-border-radius:0px;border-radius:0px}.progress-bar{float:left;width:0;height:100%;font-size:11px;color:#fff;text-align:center;background-color:#428bca;font-weight:bold;text-shadow:0 -1px 0 rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 1s ease-in-out;transition:width 1s ease-in-out}.progress-striped .progress-bar{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-danger{background-color:#a52521}.progress-striped .progress-bar-danger{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-success{background-color:#4f9e4f}.progress-striped .progress-bar-success{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-warning{background-color:#e28a0d}.progress-striped .progress-bar-warning{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-info{background-color:#316490}.progress-striped .progress-bar-info{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-info .bar,.progress .bar-info{background:#316490}.vertical-bars{padding:0;margin:0}.vertical-bars:after{content:"";display:block;height:0;clear:both}.vertical-bars li{padding:14px 0;width:25%;display:block;float:left;text-align:center}.vertical-bars li:first-child{border-left:none}.vertical-bars>li>.progress.vertical:first-child{margin-left:auto}.vertical-bars>li>.progress.vertical{margin:0 auto;float:none}.nav-tabs{border-bottom:none}.nav-tabs>li>a .badge{font-size:11px;padding:3px 5px 3px 5px;opacity:.5;margin-left:5px;min-width:17px;font-weight:normal}.tabs-left .nav-tabs>li>a .badge{margin-right:5px;margin-left:0px}.nav-tabs>li>a .label{display:inline-block;font-size:11px;margin-left:5px;opacity:.5}.nav-tabs>li>a{color:#adadad;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.nav-tabs>li>a:hover{color:#1d1d1d;border-color:transparent transparent #adadad transparent;margin-top:1px;border-top-width:0}.nav-tabs>li.active>a{background-color:#adadad;color:#2b2b2b;border-top-width:0px !important;margin-top:1px !important;font-weight:bold}.tabs-left .nav-tabs>li.active>a{-webkit-box-shadow:-2px 0 0 #428bca;-moz-box-shadow:-2px 0 0 #428bca;box-shadow:-2px 0 0 #428bca;border-top-width:1px !important;border-left:none !important;margin-left:1px !important}.tabs-left .nav-pills>li.active>a{border:none !important;box-shadow:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important}.tabs-right .nav-tabs>li.active>a{-webkit-box-shadow:2px 0 0 #428bca;-moz-box-shadow:2px 0 0 #428bca;box-shadow:2px 0 0 #428bca;border-top-width:1px !important;border-right:none !important;margin-right:1px !important}.tabs-below .nav-tabs>li.active>a{-webkit-box-shadow:0 2px 0 #428bca;-moz-box-shadow:0 2px 0 #428bca;box-shadow:0 2px 0 #428bca;border-bottom-width:0px !important;border-top:none !important;margin-top:0px !important}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #9b9b9b}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li,.tabs-left>.nav-pills>li,.tabs-right>.nav-pills>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a,.tabs-left>.nav-pills>li>a,.tabs-right>.nav-pills>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs,.tabs-left>.nav-pills{float:left;margin-right:19px;border-right:1px solid #9b9b9b}.tabs-left>.nav-pills{border-right:none}.tabs-left>.nav-tabs>li>a{margin-right:-1px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#adadad #949494 #adadad #adadad}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#949494 transparent #949494 #9b9b9b;*border-right-color:#fff}.tabs-left>.tab-content{margin-left:109px}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #9b9b9b}.tabs-right>.nav-tabs>li>a{margin-left:-1px}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#adadad #adadad #adadad #9b9b9b}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#9b9b9b #9b9b9b #9b9b9b transparent;*border-left-color:#fff}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #9b9b9b}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#9b9b9b;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #9b9b9b #9b9b9b #9b9b9b}.nav-tabs.bordered{background:#fff;border:1px solid #9b9b9b}.nav-tabs.bordered>:first-child a{border-left-width:0px !important}.nav-tabs.bordered+.tab-content{border:1px solid #9b9b9b;border-top:none}.tabs-pull-right.nav-tabs>li,.tabs-pull-right.nav-pills>li{float:right}.tabs-pull-right.nav-tabs>li:first-child>a,.tabs-pull-right.nav-pills>li:first-child>a{margin-right:1px}.tabs-pull-right.bordered.nav-tabs>li:first-child>a,.tabs-pull-right.bordered.nav-pills>li:first-child>a{border-left-width:1px !important;margin-right:0px;border-right-width:0px}.dropdown-menu-xs{min-width:37px}.dropdown-menu-xs>li>a{padding:3px 10px}.dropdown-menu-xs>li>a:hover i{color:#fff !important}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#2b2b2b;margin-top:5px;margin-right:-10px}.dropdown-submenu:hover>a:after{border-left-color:#adadad}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px}.pagination>li>a,.pagination>li>span{box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05);-moz-box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05);-webkit-box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05)}.btn-default.disabled{color:#adadad}.btn{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;will-change:background-color, border-color;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-transition:all 0.18s ease-in-out;transition:all 0.18s ease-in-out}.btn.btn-ribbon{background-color:#707070;background-image:-moz-linear-gradient(top, #777, #666);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#777), to(#666));background-image:-webkit-linear-gradient(top, #777, #666);background-image:-o-linear-gradient(top, #777, #666);background-image:linear-gradient(to bottom, #777777,#666666);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff777777', endColorstr='#ff666666', GradientType=0);color:white;padding:0 5px;line-height:20px;vertical-align:middle;height:20px;display:block;border:none;float:left;margin:0 8px 0 0;cursor:pointer}.btn.btn-ribbon>i{font-size:111%}.ribbon-button-alignment{padding-top:10px;display:inline-block}.ribbon-button-alignment.pull-right>.btn.btn-ribbon{margin:0 0 0 8px}.panel-purple{border-color:#6e587a}.panel-purple>.panel-heading{color:#fff;background-color:#6e587a;border-color:#6e587a}.panel-greenLight{border-color:#71843f}.panel-greenLight>.panel-heading{color:#fff;background-color:#71843f;border-color:#71843f}.panel-greenDark{border-color:#496949}.panel-greenDark>.panel-heading{color:#fff;background-color:#496949;border-color:#496949}.panel-darken{border-color:#313335}.panel-darken>.panel-heading{color:#fff;background-color:#404040;border-color:#404040}.panel-pink{border-color:#e06fdf}.panel-pink>.panel-heading{color:#fff;background-color:#e06fdf;border-color:#e06fdf}.panel-green{border-color:#5cb85c}.panel-green>.panel-heading{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.panel-blueLight{border-color:#92a2a8}.panel-blueLight>.panel-heading{color:#fff;background-color:#92a2a8;border-color:#92a2a8}.panel-pinkDark{border-color:#a8829f}.panel-pinkDark>.panel-heading{color:#fff;background-color:#a8829f;border-color:#a8829f}.panel-redLight{border-color:#a65858}.panel-redLight>.panel-heading{color:#fff;background-color:#a65858;border-color:#a65858}.panel-red{border-color:#d9534f}.panel-red>.panel-heading{color:#fff;background-color:#d9534f;border-color:#d9534f}.panel-teal{border-color:#568a89}.panel-teal>.panel-heading{color:#fff;background-color:#568a89;border-color:#568a89}.panel-orange{border-color:#e28a0d}.panel-orange>.panel-heading{color:#fff;background-color:#e28a0d;border-color:#e28a0d}.panel-blueDark{border-color:#4c4f53}.panel-blueDark>.panel-heading{color:#fff;background-color:#4c4f53;border-color:#4c4f53}.panel-magenta{border-color:#6e3671}.panel-magenta>.panel-heading{color:#fff;background-color:#6e3671;border-color:#6e3671}.panel-blue{border-color:#428bca}.panel-blue>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-footer>.btn-block{border-radius:0px;-moz-border-radius:0px;-webkit-border-radius:0px;border-bottom:none;border-left:none;border-right:none}.btn-circle{width:30px;height:30px;text-align:center;padding:6px 0;font-size:12px;line-height:18px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%;-webkit-box-shadow:0 1px 6px 0 rgba(0,0,0,0.12),0 1px 6px 0 rgba(0,0,0,0.12);box-shadow:0 1px 6px 0 rgba(0,0,0,0.12),0 1px 6px 0 rgba(0,0,0,0.12)}.btn-circle.btn-sm,.btn-group-sm>.btn-circle.btn{width:22px;height:22px;padding:4px 0;font-size:12px;line-height:14px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-circle.btn-lg,.btn-group-lg>.btn-circle.btn{width:50px;height:50px;padding:10px 15px;font-size:18px;line-height:30px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 15px;font-size:24px;line-height:50px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-metro{margin:0 0 20px;padding-top:15px;padding-bottom:15px}.btn-metro>span{display:block;vertical-align:bottom;margin-top:10px;text-transform:uppercase}.btn-metro>span.label{position:absolute;top:0px;right:0px}.btn-label{position:relative;left:-8px;display:inline-block;padding:5px 8px;background:rgba(0,0,0,0.15);border-radius:3px 0 0 3px}.btn-labeled{padding-top:0;padding-bottom:0;padding-left:8px}.btn-link{box-shadow:none;-webkit-box-shadow:none;font-size:13px}.morris-hover.morris-default-style{border-radius:5px;padding:5px;color:#666;background:rgba(29,29,29,0.9);border:solid 2px #375959;font-family:'Oxygen Bold';font-size:10px;text-align:left;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold}.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap}.morris-hover{position:absolute;z-index:903}.fixed-page-footer .morris-hover{z-index:900}.txt-color.txt-color-blue,.txt-color-blue.pf-help-light,.pf-help-light:hover,.txt-color-blue.pf-help,.pf-help:hover,.txt-color.pf-help-default:hover,.dataTable td.pf-table-action-cell>.pf-help-default.pf-table-action-icon-cell:hover,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help-light:hover,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help:hover,.pf-landing .pf-landing-list li>i.pf-help-default:hover,.pf-landing .pf-landing-list li>i.pf-help-light:hover,.pf-landing .pf-landing-list li>i.pf-help:hover,.dataTable td.pf-table-action-cell>.txt-color-blue.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blue{color:#428bca !important}.txt-color.txt-color-blueLight,.txt-color-blueLight.pf-help-light,.txt-color-blueLight.pf-help,.dataTable td.pf-table-action-cell>.txt-color-blueLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blueLight{color:#92a2a8 !important}.txt-color.txt-color-blueDark,.txt-color-blueDark.pf-help-light,.txt-color-blueDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-blueDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blueDark{color:#4c4f53 !important}.txt-color.txt-color-grayLightest,.txt-color-grayLightest.pf-help-light,.txt-color-grayLightest.pf-help,.dataTable td.pf-table-action-cell>.txt-color-grayLightest.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayLightest{color:#eaeaea !important}.txt-color.txt-color-grayLighter,.txt-color-grayLighter.pf-help-light,.txt-color-grayLighter.pf-help,.dataTable td.pf-table-action-cell>.txt-color-grayLighter.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayLighter{color:#adadad !important}.txt-color.txt-color-grayLight,.pf-help-light,.txt-color-grayLight.pf-help,.dataTable td.pf-table-action-cell>.txt-color-grayLight.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help-light,.pf-landing .pf-landing-list li>i.txt-color-grayLight,.pf-landing .pf-landing-list li>i.pf-help-light{color:#63676a !important}.txt-color.txt-color-gray,.txt-color-gray.pf-help-light,.pf-help,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-gray,.pf-landing .pf-landing-list li>i.pf-help{color:#3c3f41 !important}.txt-color.txt-color-grayDark,.txt-color-grayDark.pf-help-light,.txt-color-grayDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-grayDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayDark{color:#313335 !important}.txt-color.txt-color-greenLight,.txt-color-greenLight.pf-help-light,.txt-color-greenLight.pf-help,.dataTable td.pf-table-action-cell>.txt-color-greenLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-greenLight{color:#66c84f !important}.txt-color.txt-color-green,.txt-color-green.pf-help-light,.pf-help-light.pf-log-info,.txt-color-green.pf-help,.pf-help.pf-log-info,.dataTable td.pf-table-action-cell>.txt-color-green.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-info,.txt-color.pf-log-info,.pf-landing .pf-landing-list li>i.pf-log-info,.pf-landing .pf-landing-list li>i.txt-color-green{color:#5cb85c !important}.txt-color.txt-color-greenDark,.txt-color-greenDark.pf-help-light,.txt-color-greenDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-greenDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-greenDark{color:#4f9e4f !important}.txt-color.txt-color-redLight,.txt-color-redLight.pf-help-light,.txt-color-redLight.pf-help,.dataTable td.pf-table-action-cell>.txt-color-redLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-redLight{color:#a65858 !important}.txt-color.txt-color-red,.txt-color-red.pf-help-light,.pf-help-light.pf-log-error,.txt-color-red.pf-help,.pf-help.pf-log-error,.dataTable td.pf-table-action-cell>.txt-color-red.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-error,.txt-color.pf-log-error,.pf-landing .pf-landing-list li>i.pf-log-error,.pf-landing .pf-landing-list li>i.txt-color-red{color:#d9534f !important}.txt-color.txt-color-redDarker,.txt-color-redDarker.pf-help-light,.txt-color-redDarker.pf-help,.dataTable td.pf-table-action-cell>.txt-color-redDarker.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-redDarker{color:#a52521 !important}.txt-color.txt-color-yellow,.txt-color-yellow.pf-help-light,.txt-color-yellow.pf-help,.dataTable td.pf-table-action-cell>.txt-color-yellow.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-yellow{color:#e2ce48 !important}.txt-color.txt-color-orangeLight,.txt-color-orangeLight.pf-help-light,.txt-color-orangeLight.pf-help,.dataTable td.pf-table-action-cell>.txt-color-orangeLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orangeLight{color:#f0ad4e !important}.txt-color.txt-color-orange,.txt-color-orange.pf-help-light,.txt-color-orange.pf-help,.dataTable td.pf-table-action-cell>.txt-color-orange.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell:hover>.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orange{color:#e28a0d !important}.txt-color.txt-color-orangeDark,.txt-color-orangeDark.pf-help-light,.txt-color-orangeDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-orangeDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orangeDark{color:#c2760c !important}.txt-color.txt-color-pink,.txt-color-pink.pf-help-light,.txt-color-pink.pf-help,.dataTable td.pf-table-action-cell>.txt-color-pink.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-pink{color:#e06fdf !important}.txt-color.txt-color-pinkDark,.txt-color-pinkDark.pf-help-light,.txt-color-pinkDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-pinkDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-pinkDark{color:#a8829f !important}.txt-color.txt-color-purple,.txt-color-purple.pf-help-light,.txt-color-purple.pf-help,.dataTable td.pf-table-action-cell>.txt-color-purple.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-purple{color:#6e587a !important}.txt-color.txt-color-darken,.txt-color-darken.pf-help-light,.txt-color-darken.pf-help,.dataTable td.pf-table-action-cell>.txt-color-darken.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-darken{color:#404040 !important}.txt-color.txt-color-lighten,.txt-color-lighten.pf-help-light,.txt-color-lighten.pf-help,.dataTable td.pf-table-action-cell>.txt-color-lighten.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-lighten{color:#d5e7ec !important}.txt-color.txt-color-white,.txt-color-white.pf-help-light,.txt-color-white.pf-help,.dataTable td.pf-table-action-cell>.txt-color-white.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-white{color:#fff !important}.txt-color.txt-color-magenta,.txt-color-magenta.pf-help-light,.txt-color-magenta.pf-help,.dataTable td.pf-table-action-cell>.txt-color-magenta.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-magenta{color:#6e3671 !important}.txt-color.txt-color-tealLighter,.txt-color-tealLighter.pf-help-light,.txt-color-tealLighter.pf-help,.dataTable td.pf-table-action-cell>.txt-color-tealLighter.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i{color:#568a89 !important}.txt-color.txt-color-indigoDark,.txt-color-indigoDark.pf-help-light,.txt-color-indigoDark.pf-help,.dataTable td.pf-table-action-cell>.txt-color-indigoDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-indigoDark{color:#5c6bc0 !important}.txt-color.txt-color-indigoDarkest,.txt-color-indigoDarkest.pf-help-light,.txt-color-indigoDarkest.pf-help,.dataTable td.pf-table-action-cell>.txt-color-indigoDarkest.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-indigoDarkest{color:#313966 !important}.txt-color.txt-color-primary,.txt-color-primary.pf-help-light,.txt-color-primary.pf-help,.dataTable td.pf-table-action-cell>.txt-color-primary.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-primary{color:#375959 !important}.txt-color.txt-color-success,.txt-color-success.pf-help-light,.txt-color-success.pf-help,.dataTable td.pf-table-action-cell>.txt-color-success.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-success{color:#4f9e4f !important}.txt-color.txt-color-information,.txt-color-information.pf-help-light,.txt-color-information.pf-help,.dataTable td.pf-table-action-cell>.txt-color-information.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-information{color:#316490 !important}.txt-color.txt-color-warning,.txt-color-warning.pf-help-light,.pf-help-light.pf-log-warning,.txt-color-warning.pf-help,.pf-help.pf-log-warning,.dataTable td.pf-table-action-cell>.txt-color-warning.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-warning,.txt-color.pf-log-warning,.pf-landing .pf-landing-list li>i.pf-log-warning,.pf-landing .pf-landing-list li>i.txt-color-warning{color:#e28a0d !important}.txt-color.txt-color-danger,.txt-color-danger.pf-help-light,.txt-color-danger.pf-help,.dataTable td.pf-table-action-cell>.txt-color-danger.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-danger{color:#a52521 !important}.bg-color.bg-color-blue{background-color:#428bca !important}.bg-color.bg-color-blueLight{background-color:#92a2a8 !important}.bg-color.bg-color-blueDark{background-color:#4c4f53 !important}.bg-color.bg-color-green{background-color:#5cb85c !important}.bg-color.bg-color-greenLight{background-color:#71843f !important}.bg-color.bg-color-greenDark{background-color:#496949 !important}.bg-color.bg-color-red{background-color:#d9534f !important}.bg-color.bg-color-yellow{background-color:#e2ce48 !important}.bg-color.bg-color-orange{background-color:#e28a0d !important}.bg-color.bg-color-orangeDark{background-color:#c2760c !important}.bg-color.bg-color-pink{background-color:#e06fdf !important}.bg-color.bg-color-pinkDark{background-color:#a8829f !important}.bg-color.bg-color-purple{background-color:#6e587a !important}.bg-color.bg-color-darken{background-color:#404040 !important}.bg-color.bg-color-lighten{background-color:#d5e7ec !important}.bg-color.bg-color-white{background-color:#fff !important}.bg-color.bg-color-gray{background-color:#3c3f41 !important}.bg-color.bg-color-grayDark{background-color:#525252 !important}.bg-color.bg-color-grayDarker{background-color:#2b2b2b !important}.bg-color.bg-color-magenta{background-color:#6e3671 !important}.bg-color.bg-color-tealLighter{background-color:#568a89 !important}.bg-color.bg-color-tealDarker{background-color:#212C30 !important}.bg-color.bg-color-tealDarkest{background-color:#1b2326 !important}.bg-color.bg-color-redLight{background-color:#a65858 !important}body{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.pf-body{overflow:hidden}a{color:#477372;will-change:color;text-decoration:none;-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}a:hover{color:#6caead;text-decoration:none}a:focus{color:#477372}em{font-style:italic}em.pf-brand{text-transform:uppercase}.pf-font-capitalize{text-transform:capitalize}.no-padding{padding:0 !important}::-webkit-scrollbar{width:16px;height:16px}::-webkit-scrollbar-track{background-color:#2b2b2b;border-left:1px solid #313335;border-radius:2px;-webkit-transition:background-color 0.5s;transition:background-color 0.5s}::-webkit-scrollbar-thumb{height:6px;border:5px solid transparent;background-clip:padding-box;-webkit-border-radius:8px;background-color:#868c90}::-webkit-scrollbar-thumb:hover{background-color:#a1a5a8}::-webkit-scrollbar-button{width:0;height:0;display:none}::-webkit-scrollbar-corner{background-color:transparent}::selection{background:#adadad;color:#1d1d1d}::-moz-selection{background:#adadad;color:#1d1d1d}.pf-help-default,.pf-help-light,.pf-help{cursor:help;-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}.pf-dialog-icon-button,.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty,.pf-sig-table-module .pf-sig-table .fa-plus,.pf-system-route-module .pf-system-route-table td .fa-refresh,.pf-system-route-module .pf-system-route-table td .fa-search-plus{cursor:pointer;margin-top:2px;-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out}.pf-dialog-icon-button:not(.collapsed),.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty:not(.collapsed),.pf-sig-table-module .pf-sig-table .fa-plus:not(.collapsed),.pf-system-route-module .pf-system-route-table td .fa-refresh:not(.collapsed),.pf-system-route-module .pf-system-route-table td .fa-search-plus:not(.collapsed),.pf-dialog-icon-button:hover,.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty:hover,.pf-sig-table-module .pf-sig-table .fa-plus:hover,.pf-system-route-module .pf-system-route-table td .fa-refresh:hover,.pf-system-route-module .pf-system-route-table td .fa-search-plus:hover{color:#e28a0d}.pf-module-icon-button{cursor:pointer;-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out}.pf-module-icon-button:hover{color:#e28a0d !important}a.disabled{color:#777;pointer-events:none;cursor:default}.alert{will-change:opacity, transform}.editable-input optgroup[label]{background-color:#3c3f41;color:#63676a}.editable-input optgroup[label] option{background-color:#313335;color:#adadad;font-family:Consolas,monospace,Menlo,Monaco,"Courier New"}select:active,select:hover{outline:none}select:active,select:hover{outline-color:red}.select2-results [class*="col-"]{line-height:22px}.select2 ::-webkit-search-cancel-button{-webkit-appearance:none !important}.select2 .select2-selection__choice__remove{float:left}.select2 .select2-selection--multiple input{box-shadow:none !important}.dataTable th.pf-table-image-cell,.dataTable th.pf-table-image-small-cell{padding-left:0 !important;padding-right:0 !important}.dataTable th.sorting,.dataTable th.sorting_asc,.dataTable th.sorting_desc{padding-right:18px !important}.dataTable td.pf-table-action-cell{cursor:pointer}.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell{-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}.dataTable td.pf-table-image-cell{padding:0 !important}.dataTable td.pf-table-image-cell img{width:26px;box-sizing:content-box;border-left:1px solid #3c3f41;border-right:1px solid #3c3f41}.dataTable td.pf-table-image-small-cell img{width:24px;border-left:1px solid transparent;border-right:1px solid transparent}.dataTable td.pf-table-button-sm-cell{padding:0}.dataTable td.pf-table-counter-cell{color:#63676a}.dataTable td.pf-table-counter-cell .pf-digit-counter-small{width:20px;display:inline-block;font-size:10px}.dataTable td.pf-table-counter-cell .pf-digit-counter-large{width:26px;display:inline-block;font-size:10px}.dataTable td .pf-table-cell-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dataTable td .pf-table-cell-80{width:90px}.dataTable td .pf-table-cell-90{width:100px}.dataTable td .pf-table-cell-100{width:110px}.dataTable td.separator-right,.dataTable th.separator-right{border-right:1px solid #3c3f41}.dataTable td svg.peity,.dataTable th svg.peity{display:block}table tr.collapsing{-webkit-transition:height 0.01s ease;transition:height 0.01s ease}table tr.collapse.in{display:table-row !important}.pf-table-tools{height:45px}.pf-table-tools .btn:not(:last-child){margin-right:10px}.pf-table-tools-action{will-change:height, opacity, display;opacity:0;display:none;height:0;visibility:hidden}.pf-loading-overlay{position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;background:#2b2b2b;z-index:1060;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-loading-overlay .pf-loading-overlay-wrapper{width:25px;height:25px;margin:auto;text-align:center;position:absolute;top:0;left:0;bottom:0;right:0}.pf-loading-overlay .pf-loading-overlay-wrapper i{padding:3px}.navbar-nav li:not(.disabled):hover:before,.navbar-nav li:not(.disabled).active:before{top:-4px;opacity:1}.navbar-nav li:not(.disabled):before{content:'';position:absolute;width:100%;height:2px;background-color:#5cb85c;top:0;opacity:0;will-change:opacity, top;-webkit-transition:top 0.15s ease-out,opacity 0.15s ease-out;transition:top 0.15s ease-out,opacity 0.15s ease-out}.pf-navbar-version-info{cursor:pointer}.pf-site{will-change:transform}.sb-slidebar{will-change:transform}.sb-left .list-group-item{-webkit-box-shadow:inset -10px 0px 5px -5px rgba(0,0,0,0.4);box-shadow:inset -10px 0px 5px -5px rgba(0,0,0,0.4)}.sb-right .list-group-item{-webkit-box-shadow:inset 10px 0px 5px -5px rgba(0,0,0,0.4);box-shadow:inset 10px 0px 5px -5px rgba(0,0,0,0.4)}.mCSB_container,.mCSB_dragger{will-change:top, left}.pf-timestamp-counter{visibility:hidden}.pf-map-type-private{color:#7986cb}.pf-map-type-corporation{color:#5cb85c}.pf-map-type-alliance{color:#428bca}.pf-map-type-global{color:#568a89}#pf-map-module{margin:20px 10px 0 10px}#pf-map-module #pf-map-tabs .pf-map-type-tab-default{border-top:2px solid transparent}#pf-map-module #pf-map-tabs .pf-map-type-tab-private{border-top:2px solid #7986cb}#pf-map-module #pf-map-tabs .pf-map-type-tab-corporation{border-top:2px solid #5cb85c}#pf-map-module #pf-map-tabs .pf-map-type-tab-alliance{border-top:2px solid #428bca}#pf-map-module #pf-map-tabs .pf-map-type-tab-global{border-top:2px solid #568a89}#pf-map-module #pf-map-tabs .pf-map-tab-icon{margin-right:5px}#pf-map-module #pf-map-tabs .pf-map-tab-shared-icon{margin-left:5px}.pf-map-content-row{margin-top:10px;padding-bottom:40px}.pf-map-content-row .pf-module{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;background:rgba(60,63,65,0.3);padding:10px;width:100%;margin-bottom:10px;will-change:height, transform, opacity;overflow:hidden;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-map-content-row .pf-module:before{content:'';position:absolute;top:0;left:0;border-style:solid;border-width:0 0 8px 8px;border-color:transparent transparent transparent #3c3f41;cursor:pointer}.pf-map-content-row .pf-module .label{margin-bottom:10px}.pf-map-content-row .pf-module .pf-dynamic-area{background:rgba(43,43,43,0.4)}.pf-map-content-row .pf-module h5 .pf-module-icon-button{margin-left:5px}.pf-user-status{color:#a52521}.pf-user-status-corp{color:#5cb85c}.pf-user-status-ally{color:#428bca}.pf-user-status-own{color:#7986cb}.pf-system-effect{display:none;cursor:default;color:#adadad}.pf-system-effect-magnetar{color:#e06fdf;display:inline-block}.pf-system-effect-redgiant{color:#d9534f;display:inline-block}.pf-system-effect-pulsar{color:#428bca;display:inline-block}.pf-system-effect-wolfrayet{color:#e28a0d;display:inline-block}.pf-system-effect-cataclysmic{color:#ffb;display:inline-block}.pf-system-effect-blackhole{color:#000;display:inline-block}.pf-system-info-rally .pf-system-head{background-color:#782d77;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjEuMCIgeDI9IjAuMCIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiMzZTI2NGUiLz48c3RvcCBvZmZzZXQ9IjI1JSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzNlMjY0ZSIvPjxzdG9wIG9mZnNldD0iNzUlIiBzdG9wLWNvbG9yPSIjM2UyNjRlIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-moz-linear-gradient(135deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-image:-webkit-linear-gradient(135deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-image:linear-gradient(-45deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:25px 25px;-webkit-animation:move 3s linear infinite;-moz-animation:move 3s linear infinite;-ms-animation:move 3s linear infinite;animation:move 3s linear infinite}.pf-system-security-0-0{color:#be0000}.pf-system-security-0-1{color:#ab2600}.pf-system-security-0-2{color:#be3900}.pf-system-security-0-3{color:#c24e02}.pf-system-security-0-4{color:#ab5f00}.pf-system-security-0-5{color:#bebe00}.pf-system-security-0-6{color:#73bf26}.pf-system-security-0-7{color:#00bf00}.pf-system-security-0-8{color:#00bf39}.pf-system-security-0-9{color:#39bf99}.pf-system-security-1-0{color:#28c0bf}.pf-system-sec{margin-right:5px;cursor:-moz-grab;cursor:-webkit-grab}.pf-system-sec-highSec{color:#5cb85c}.pf-system-sec-lowSec{color:#e28a0d}.pf-system-sec-nullSec{color:#d9534f}.pf-system-sec-high{color:#d9534f}.pf-system-sec-mid{color:#e28a0d}.pf-system-sec-low{color:#428bca}.pf-system-sec-unknown{color:#7986cb}.pf-system-status-friendly{border-color:#428bca !important;color:#428bca}.pf-system-status-occupied{border-color:#e28a0d !important;color:#e28a0d}.pf-system-status-hostile{border-color:#d9534f !important;color:#d9534f}.pf-system-status-empty{border-color:#5cb85c !important;color:#5cb85c}.pf-system-status-unscanned{border-color:#568a89 !important;color:#568a89}.pf-system-info-status-label{background-color:#63676a;color:#000;will-change:background-color;-webkit-transition:background-color 0.5s ease-out;transition:background-color 0.5s ease-out}.pf-system-info-status-label.pf-system-status-friendly{background-color:#428bca}.pf-system-info-status-label.pf-system-status-occupied{background-color:#e28a0d}.pf-system-info-status-label.pf-system-status-hostile{background-color:#d9534f}.pf-system-info-status-label.pf-system-status-empty{background-color:#5cb85c}.pf-system-info-status-label.pf-system-status-unscanned{background-color:#568a89}.pf-system-effect-dialog-wrapper .table,.pf-jump-info-dialog .table{margin:15px 0}.pf-system-effect-dialog-wrapper .table td,.pf-jump-info-dialog .table td{text-transform:capitalize}.pf-fake-connection{box-sizing:content-box;display:inline-block;width:70px;height:4px;margin-right:5px;border-top:2px solid #63676a;border-bottom:2px solid #63676a;background-color:#3c3f41;position:relative;font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif}.pf-fake-connection.pf-map-connection-stargate{background-color:#313966;border-color:#63676a}.pf-fake-connection.pf-map-connection-jumpbridge{background-color:#6caead;border-color:#3c3f41;background:repeating-linear-gradient(to right, #6caead, #6caead 10px, #3c3f41 10px, #3c3f41 20px)}.pf-fake-connection.pf-map-connection-wh-eol{border-color:#d747d6}.pf-fake-connection.pf-map-connection-wh-reduced{background-color:#e28a0d}.pf-fake-connection.pf-map-connection-wh-critical{background-color:#a52521}.pf-fake-connection.pf-map-connection-frig{border-style:dashed;border-left:none;border-right:none}.pf-fake-connection.pf-map-connection-frig:after{content:'frig';background-color:#e28a0d;color:#1d1d1d;padding:0px 3px;position:absolute;left:25px;top:-6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.pf-fake-connection.pf-map-connection-preserve-mass:after{content:'save mass';background-color:#a52521;color:#eaeaea;padding:0px 3px;position:absolute;left:9px;top:-6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.tooltip-inner{color:#5cb85c;background-color:#3c3f41;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;padding:5px 5px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.modal .tooltip{z-index:1060}.modal .tooltip .tooltip-inner{color:#313335;background-color:#adadad}.tooltip.top .tooltip-arrow{border-top-color:#63676a}.tooltip.right .tooltip-arrow{border-right-color:#63676a}.tooltip.bottom .tooltip-arrow{border-bottom-color:#63676a}.tooltip.left .tooltip-arrow{border-left-color:#63676a}.popover{z-index:1060}.popover img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.popover h4{color:#adadad}.popover table{color:#adadad;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:16px;font-size:11px}.popover table td{padding:0 5px;vertical-align:middle !important}.pf-popover{display:initial}.pf-popover .popover-content{padding:0}.pf-popover h6{white-space:nowrap;margin-right:50px}.pf-popover h6:before,.pf-popover h6:after{content:" ";display:table}.pf-popover h6:after{clear:both}.pf-popover .well{margin-top:7px;margin-bottom:10px}.pf-popover .list-group{margin:0}.pf-popover .list-group .list-group-item{color:#313335}.pf-popover .list-group .list-group-item:hover{color:#1d1d1d}.pf-popover .list-group .list-group-item.disabled{background-color:#3c3f41;color:#63676a;cursor:not-allowed}.pf-popover .list-group .list-group-item img{width:30px;margin:-8px 10px -6px -8px;border-radius:0}.pf-popover .list-group .list-group-item i{margin-right:20px}td.pf-popover-trigger:hover{color:#477372}.pf-notransition{-webkit-transition:none !important;-moz-transition:none !important;-o-transition:none !important;transition:none !important}.pf-dynamic-area{padding:10px;min-height:100px;position:relative;background-color:#313335;overflow:hidden;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-dynamic-area .dl-horizontal{margin-bottom:0}.pf-dynamic-area .dl-horizontal dd{min-width:100px}.pf-dynamic-area .dl-horizontal dd.txt-color,.pf-dynamic-area .dl-horizontal dd.pf-help-light,.pf-dynamic-area .dl-horizontal dd.pf-help,.pf-dynamic-area .dl-horizontal .dataTable td.pf-table-action-cell>dd.pf-table-action-icon-cell,.dataTable .pf-dynamic-area .dl-horizontal td.pf-table-action-cell>dd.pf-table-action-icon-cell{font-weight:bold}#pf-logo-wrapper{display:block}#pf-head{margin-bottom:0px}#pf-head a{-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out;will-change:color}#pf-head a:focus{color:#477372}#pf-head a:focus img{border-color:#3c3f41}#pf-head a:hover{text-decoration:none}#pf-head a:hover .badge{color:#6caead}#pf-head a:hover img{border-color:#568a89}#pf-head i{margin-right:2px}#pf-head .pf-brand-desc{margin:6px 10px 0 90px;width:180px}#pf-head .pf-head-menu{padding:3px 10px;line-height:24px}#pf-head .pf-head-menu .pf-head-menu-logo{width:24px;height:24px;display:inline-block;float:left}#pf-head .pf-head-user-character,#pf-head .pf-head-user-ship{opacity:0;visibility:hidden}#pf-head .pf-head-active-user,#pf-head #pf-head-current-location{display:none}#pf-head .pf-head-active-user .badge,#pf-head #pf-head-current-location .badge{-webkit-transition:color 0.3s ease-out;transition:color 0.3s ease-out}#pf-head .pf-head-user-character-image,#pf-head .pf-head-user-ship-image{display:inline-block;margin-top:-6px;margin-bottom:-6px;width:27px;border:1px solid #3c3f41;margin-right:3px;-webkit-transition:border-color 0.15s ease-out;transition:border-color 0.15s ease-out;will-change:border-color}#pf-head .pf-head-program-status{cursor:pointer}#pf-head .navbar-text{min-width:60px}#pf-head .tooltip .tooltip-inner{color:#adadad}.pf-head{-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.pf-head .badge{background-color:#3c3f41;color:#adadad}.pf-head small{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}#pf-head-character-switch td{border:none}#pf-head-character-switch td:first-child+td{padding:0 5px}#pf-footer{position:absolute;bottom:0;left:0;width:100%;margin:0;background:rgba(60,63,65,0.3)}#pf-footer a{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;color:#375959}#pf-footer a:hover{color:#477372;text-decoration:none}.navbar-fixed-bottom{padding:2px 0}.navbar-fixed-bottom .container-fluid{padding-left:0;padding-right:0}#pf-global-info{position:absolute;left:0;bottom:32px;width:100%;height:32px;margin-bottom:0}@-webkit-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@-moz-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@-ms-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}.pf-animate{visibility:hidden;opacity:0}.pf-color-line{position:fixed;top:0;left:0;width:100%;height:3px;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2Yzg0ZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzY2Yzg0ZiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #66c84f),color-stop(100%, #66c84f));background-image:-moz-linear-gradient(left, #66c84f,#66c84f 100%);background-image:-webkit-linear-gradient(left, #66c84f,#66c84f 100%);background-image:linear-gradient(to right, #66c84f,#66c84f 100%)}.pf-color-line.warning{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2UyOGEwZCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2UyOGEwZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #e28a0d),color-stop(100%, #e28a0d));background-image:-moz-linear-gradient(left, #e28a0d,#e28a0d 100%);background-image:-webkit-linear-gradient(left, #e28a0d,#e28a0d 100%);background-image:linear-gradient(to right, #e28a0d,#e28a0d 100%)}.pf-color-line.danger{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E1MjUyMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2E1MjUyMSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #a52521),color-stop(100%, #a52521));background-image:-moz-linear-gradient(left, #a52521,#a52521 100%);background-image:-webkit-linear-gradient(left, #a52521,#a52521 100%);background-image:linear-gradient(to right, #a52521,#a52521 100%)}.pf-splash{position:absolute;z-index:2000;background-color:#1d1d1d;color:#63676a;top:0;bottom:0;left:0;right:0;will-change:opacity}.pf-splash .pf-splash-title{position:fixed;left:50%;top:30%;text-align:center;max-width:500px;padding:20px;-moz-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}@media (max-width: 1200px){.pf-landing #pf-logo-container{margin:5px auto}.pf-landing .pf-brand-desc{display:none}.pf-landing .navbar .navbar-brand{margin-left:10px}}.pf-landing section{min-height:200px;padding:20px 0 40px 0;border-bottom:1px solid #2b2b2b}.pf-landing section h4{font-size:18px;font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;margin:5px 0 10px 0;border-bottom:1px solid #2b2b2b;line-height:34px}.pf-landing .container>.row{margin-bottom:30px}.pf-landing .alert{box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing a[data-gallery]{position:relative}.pf-landing a[data-gallery]:before{content:'\f002';font-family:'FontAwesome';font-size:20px;line-height:20px;color:#e28a0d;position:absolute;top:9px;left:8px;height:100%;width:100%;padding-top:calc(50% - 10px);z-index:10;text-align:center;-webkit-transition:transform 0.1s 0.06s ease-in,opacity 0.1s ease-out;transition:transform 0.1s 0.06s ease-in,opacity 0.1s ease-out;will-change:transform, opacity;transform:scale(0, 0);opacity:0}.pf-landing a[data-gallery]:hover img{border-color:#6caead;-webkit-filter:brightness(50%);filter:brightness(50%)}.pf-landing a[data-gallery]:hover:before{-webkit-transition-delay:.1s;transition-delay:.1s;transform:scale(1, 1);opacity:1}.pf-landing a[data-gallery] .pf-landing-image-preview{border-width:1px;border-style:solid;border-color:#1d1d1d;margin:5px 0 15px 0;display:inline-block;will-change:all;-webkit-filter:brightness(100%);filter:brightness(100%);-webkit-transition:all 0.2s ease-out;transition:all 0.2s ease-out;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing a[data-gallery] .pf-landing-image-preview.pf-landing-image-preview-small{height:160px}.pf-landing a[data-gallery] .pf-landing-image-preview.pf-landing-image-preview-medium{height:256px}#pf-landing-top{height:355px;border-bottom:1px solid #313335;position:relative}#pf-landing-top:before{content:'';width:100%;height:100%;position:absolute;background:url("../img/pf-bg.jpg") #05050a;background-repeat:no-repeat;background-position:0 0;-webkit-filter:brightness(.9);filter:brightness(.9)}#pf-landing-top #pf-logo-container{-moz-transform:scale3d(0.8, 0.8, 1);-ms-transform:scale3d(0.8, 0.8, 1);-webkit-transform:scale3d(0.8, 0.8, 1);transform:scale3d(0.8, 0.8, 1)}#pf-landing-top #pf-header-container{position:absolute;width:100%;background-position:center center}#pf-landing-top #pf-header-container #pf-header-canvas{position:absolute;visibility:hidden;top:0;left:0}#pf-landing-top #pf-header-container #pf-logo-container{z-index:110}#pf-landing-top #pf-header-container #pf-header-preview-container{position:absolute;left:400px;width:590px;height:350px;top:37px}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element{position:relative;margin-left:12px;margin-top:12px;height:155px;width:180px;padding:7px;opacity:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;background-color:rgba(43,43,43,0.5)}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element:nth-child(n+4){box-shadow:0 4px 10px rgba(0,0,0,0.4)}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element:after{content:'';position:absolute;width:calc(100% - 14px);height:calc(100% - 14px);-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-repeat:no-repeat;background-position:50% 50%;background-color:rgba(29,29,29,0.75)}#pf-landing-top .container{position:relative;margin-top:10px}#pf-header-preview-intel:after{background-image:url("../img/landing/intel.png")}#pf-header-preview-map:after{background-image:url("../img/landing/map.png")}#pf-header-preview-scope:after{background-image:url("../img/landing/scope.png")}#pf-header-preview-signature:after{background-image:url("../img/landing/signature.png")}#pf-header-preview-data:after{background-image:url("../img/landing/data.png")}#pf-header-preview-gameplay:after{background-image:url("../img/landing/gameplay.png")}#pf-landing-login{padding-top:40px;padding-bottom:30px}#pf-landing-login .row{margin-bottom:0}#pf-landing-login .pf-character-selection>div:not(.pf-character-row-animate){-webkit-transition:width 0.2s ease,margin 0.2s ease;transition:width 0.2s ease,margin 0.2s ease}#pf-landing-login .pf-dynamic-area{display:inline-block;margin:10px 5px 20px 5px;padding:10px 10px 5px 10px;min-width:155px;min-height:184px;overflow:visible;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}#pf-landing-login .pf-dynamic-area .ribbon-wrapper{z-index:5}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper{opacity:0;width:128px;border:2px solid #63676a;-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px;-webkit-transition:border-color 0.2s ease-out,box-shadow 0.2s ease-out;transition:border-color 0.2s ease-out,box-shadow 0.2s ease-out;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);will-change:border-color, transition;overflow:hidden;cursor:pointer;display:inline-block;background-color:#2b2b2b;box-sizing:content-box}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover{border-color:#4f9e4f}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover .pf-character-name{color:#4f9e4f}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover .pf-character-image{-webkit-filter:grayscale(50%);filter:grayscale(50%)}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image{overflow:hidden;width:128px;height:128px;position:relative}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image .pf-character-info{position:absolute;top:0;left:0;width:0;height:100%;color:#adadad;background:rgba(60,63,65,0.8);overflow:hidden;will-change:width, transition;padding:10px 0}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image .pf-character-info .pf-character-info-text{line-height:25px}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-name{font-size:13px;line-height:30px;border-top:1px solid #313335;color:#adadad;-webkit-transition:color 0.2s ease-out;transition:color 0.2s ease-out}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-image{-webkit-transition:all 0.3s ease-out;transition:all 0.3s ease-out;-webkit-filter:grayscale(0%);filter:grayscale(0%)}#pf-landing-login .pf-sso-login-button{position:relative;display:inline-block;width:270px;height:45px;border:none;margin-bottom:10px;background-color:transparent;background-image:url("../img/landing/eve_sso_login_buttons_large_black_hover.png");cursor:pointer;box-shadow:0 2px 5px rgba(0,0,0,0.2);-webkit-transition:box-shadow 0.12s ease-out;transition:box-shadow 0.12s ease-out;will-change:box-shadow}#pf-landing-login .pf-sso-login-button:after{content:' ';position:absolute;width:270px;height:45px;left:0;top:0;background-image:url("../img/landing/eve_sso_login_buttons_large_black.png");-webkit-transition:opacity 0.12s ease-in-out;transition:opacity 0.12s ease-in-out;will-change:opacity}#pf-landing-login .pf-sso-login-button:hover{box-shadow:0 4px 5px rgba(0,0,0,0.2)}#pf-landing-login .pf-sso-login-button:hover:after{opacity:0}#pf-landing-login .pf-sso-login-button.disabled{pointer-events:auto}#pf-landing-login #pf-notification-panel{display:none}#pf-header-map{position:relative;margin:0 auto;height:380px;width:600px;pointer-events:none}#pf-header-map .pf-header-svg-layer{position:absolute;top:0;left:0;right:0;bottom:0}#pf-header-map #pf-header-systems{z-index:100}#pf-header-map #pf-header-connectors{z-index:90}#pf-header-map #pf-header-connections{z-index:80}#pf-header-map #pf-header-background{z-index:70}#pf-header-map #pf-header-background .pf-header-system{display:none}#pf-header-map-bg{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}#pf-header-map-bg img{pointer-events:none}#pf-header-map-bg #pf-map-bg-image{opacity:0;position:absolute;bottom:0;right:0;width:100%;height:100%}#pf-header-map-bg #pf-map-neocom{opacity:0;height:665px;width:21px}#pf-header-map-bg #pf-map-browser{opacity:0;position:absolute;top:110px;left:21px;height:560px;width:515px}#pf-landing-gallery-carousel{background-image:url("../img/pf-header-bg.jpg")}#pf-landing-gallery-carousel .slide-content{border-radius:5px;pointer-events:none}#pf-landing-gallery-carousel h3{width:100%;text-align:left}.pf-landing-pricing-panel{margin-top:20px}.pricing-big{-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pricing-big .panel-heading{border-color:#3c3f41}.pricing-big .the-price{padding:1px 0;background:#2d3031;text-align:center}.pricing-big .the-price .subscript{font-size:12px;color:#63676a}.pricing-big .price-features{background:#3c3f41;color:#adadad;padding:20px 15px;line-height:22px}.pricing-big .price-features:not(.price-features-fluid){min-height:205px}.pricing-big .price-features .list-unstyled.text-left li,.pricing-big .price-features .text-left.list-inline li{text-indent:-1em;padding-left:1.5em}.pricing-big .price-features .list-unstyled.text-left li .fa,.pricing-big .price-features .text-left.list-inline li .fa,.pricing-big .price-features .list-unstyled.text-left .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pricing-big .price-features .list-unstyled.text-left li>i,.pricing-big .price-features .text-left.list-inline .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pricing-big .price-features .text-left.list-inline li>i{text-indent:0}.pricing-big table tr td{line-height:1}#pf-landing-admin .pf-landing-admin-login{margin-bottom:0}#pf-landing-about .pf-landing-about-me{width:256px;height:256px;border:none;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing-footer{padding:30px 0;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:#171717}.pf-landing-footer .row{margin-bottom:0 !important}.pf-landing-footer .pf-social-networks>li{display:inline-block;line-height:1}.pf-landing-footer .pf-social-networks>li a{display:inline-block;background:rgba(99,103,106,0.5);line-height:24px;text-align:center;font-size:12px;margin-right:5px;width:28px;height:24px}#pf-static-logo-svg{opacity:0;position:absolute;z-index:105;overflow:visible}#pf-static-logo-svg path{will-change:fill, opacity, transform, translateZ, translateX, translateY;pointer-events:all;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.logo-ploygon-top-right{fill:#477372;fill-rule:evenodd;stroke:#477372;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-bottom-left{fill:#5cb85c;fill-rule:evenodd;stroke:#5cb85c;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-bottom-right{fill:#375959;fill-rule:evenodd;stroke:#375959;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-top-left{fill:#63676a;fill-opacity:1;fill-rule:evenodd;stroke:#63676a;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1}@-webkit-keyframes bounce{0%, 20%, 50%, 80%, 100%{-webkit-transform:translateY(0)}40%{-webkit-transform:translateY(-8px)}60%{-webkit-transform:translateY(-4px)}}@keyframes bounce{0%, 20%, 50%, 80%, 100%{transform:translateY(0)}40%{transform:translateY(-8px)}60%{transform:translateY(-4px)}}#pf-map-tab-element{max-width:2515px;margin:0 auto}.pf-map-tab-content .pf-map-wrapper{position:relative;width:100%;max-width:2515px;height:550px;overflow:auto;padding:5px;background:rgba(43,43,43,0.93);box-shadow:inset -3px 3px 10px 0 rgba(0,0,0,0.3);border-bottom-right-radius:5px;border-bottom-left-radius:5px;border-width:1px;border-style:solid;border-color:#313335}.pf-map-tab-content .pf-map-wrapper:focus{border:1px solid #3c3f41}.pf-map-overlay{position:absolute;display:none;z-index:10000;right:10px;background:rgba(0,0,0,0.25);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-map-overlay.pf-map-overlay-timer{bottom:23px;width:36px;height:36px}.pf-map-overlay.pf-map-overlay-info{top:8px;height:36px;min-height:36px;min-width:36px;color:#2b2b2b;padding:3px;line-height:26px}.pf-map-overlay.pf-map-overlay-info i{margin:0;margin-top:3px;width:0;height:26px;opacity:0;color:#63676a;transform:scale(0);transform-origin:50% 50% 0px;-webkit-transition:color 0.18s ease-in-out;transition:color 0.18s ease-in-out;cursor:help;will-change:all}.pf-map-overlay.pf-map-overlay-info i.fa,.pf-map-overlay.pf-map-overlay-info .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pf-map-overlay.pf-map-overlay-info li>i{font-size:26px}.pf-map-overlay.pf-map-overlay-info i.glyphicon{margin-top:1px;font-size:24px;padding-left:3px}.pf-map-overlay.pf-map-overlay-info i.active,.pf-map-overlay.pf-map-overlay-info i:hover{color:#c2760c}.pf-map-overlay.pf-map-overlay-local{top:54px;min-height:80px;width:32px;display:block;will-change:width}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content{margin-right:36px;padding:5px 0 5px 5px;overflow:hidden}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline{font-size:12px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline .badge{margin-left:5px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline .pf-system-sec{cursor:default}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-local-table{font-size:10px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-local-table td{white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .dataTables_paginate,.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .dataTables_empty{white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-toolbar .pf-map-overlay-toolbar-icon{vertical-align:0;margin-top:14px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-toolbar .pf-map-overlay-toolbar-checkbox{display:inline-block;margin-bottom:0}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main{position:absolute;top:0;right:0;height:100%;padding:3px;width:32px;cursor:pointer;text-align:center;border-left:1px solid #2b2b2b}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main .pf-map-overlay-local-trigger{margin-bottom:10px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main i{font-size:12px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-jumps{position:absolute;bottom:5px;width:calc(100% - 6px)}.pf-map-overlay.pf-map-overlay-local .badge{font-family:Arial, sans-serif;background-color:#2b2b2b}.pf-grid-small{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAG1JREFUeNrs18EJgDAQRNGJpoQQSC+CWMSWEwhYrCAWYRNz2MP/BQzvOiUi5Op5vzl6u+VrbUoeQIAAAQIECBAgQICpK8d5zay40dtenR+CTwIQIECAAAECBAgQYLaqpGX8EHLuSdIPAAD//wMAuMQN2uF+ypQAAAAASUVORK5CYII=') !important}.pf-map{width:2500px;height:520px;position:relative;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.pf-map .jsplumb-overlay{opacity:1;pointer-events:none;will-change:opacity;-webkit-transition:opacity 0.18s ease-out;transition:opacity 0.18s ease-out}.pf-map .jsplumb-hover.jsplumb-overlay{opacity:0 !important}.pf-map .jsplumb-hover:not(.jsplumb-overlay){-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-webkit-animation-name:bounce;animation-name:bounce}.pf-map .jsplumb-target-hover,.pf-map .jsplumb-source-hover{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-webkit-animation-name:bounce;animation-name:bounce;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.3);box-shadow:0 6px 12px rgba(0,0,0,0.3)}.pf-map .pf-system{position:absolute;min-width:60px;height:auto;overflow:hidden;background-color:#313335;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;z-index:100;will-change:top, left, opacity;border-width:2px;border-style:solid;border-color:#63676a;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-webkit-transition:border-color 0.5s ease-out,box-shadow 0.2s ease-out;transition:border-color 0.5s ease-out,box-shadow 0.2s ease-out;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.pf-map .pf-system:hover{-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.3);box-shadow:0 6px 12px rgba(0,0,0,0.3);-moz-transform:translate3d(0, -2px, 0);-ms-transform:translate3d(0, -2px, 0);-webkit-transform:translate3d(0, -2px, 0);transform:translate3d(0, -2px, 0)}.pf-map .pf-system .pf-system-head{padding:0px 3px 0px 3px;cursor:pointer;font-family:Arial, sans-serif;font-weight:bold}.pf-map .pf-system .pf-system-head .pf-system-head-name{border:none;display:inline-block;min-width:41px;color:#adadad;margin-right:2px}.pf-map .pf-system .pf-system-head .fa-lock{display:none}.pf-map .pf-system .pf-system-head .pf-system-head-expand{margin-left:2px;color:#63676a;display:none}.pf-map .pf-system .pf-system-head .editable-empty{font-style:normal}.pf-map .pf-system .pf-system-body{height:0px;width:100%;overflow:hidden;cursor:-moz-grab;cursor:-webkit-grab;cursor:grab;padding:0 4px;white-space:nowrap;display:none;will-change:width;border-top-width:1px;border-top-style:dashed;border-top-color:#63676a}.pf-map .pf-system .pf-system-body .pf-system-body-item{color:#7c8184;font-size:10px;line-height:16px;height:16px}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-system-body-right{text-overflow:ellipsis;float:right;color:#f0ad4e;display:none}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-user-status{font-size:7px;width:10px;vertical-align:middle;height:14px}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-system-body-item-name{display:inline-block;width:65px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.pf-map .pf-system .tooltip.in{opacity:1}.pf-map .pf-system .tooltip .tooltip-inner{color:#313335;background-color:#adadad;padding:3px 3px}.pf-map .pf-system-active:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target){-webkit-box-shadow:#ffb 0px 0px 8px 0px;box-shadow:#ffb 0px 0px 8px 0px}.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target),.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target){-webkit-box-shadow:#58100d 0px 0px 8px 0px;box-shadow:#58100d 0px 0px 8px 0px}.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-head,.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-head,.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-body,.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-body{background-color:#58100d}.pf-map .pf-system-locked .pf-system-sec{cursor:default !important}.pf-map .pf-system-locked .pf-system-body{cursor:default !important}.pf-map .pf-system-locked .fa-lock{color:#63676a !important;display:inline-block !important}.pf-map .pf-map-endpoint-source,.pf-map .pf-map-endpoint-target{z-index:90}.pf-map .pf-map-endpoint-source svg,.pf-map .pf-map-endpoint-target svg{overflow:visible}.pf-map .pf-map-endpoint-source svg circle,.pf-map .pf-map-endpoint-target svg circle{-webkit-transition:stroke 0.18s ease-out,fill 0.18s ease-out;transition:stroke 0.18s ease-out,fill 0.18s ease-out}.pf-map .pf-map-endpoint-source svg *,.pf-map .pf-map-endpoint-target svg *{stroke:#63676a;stroke-width:2;fill:#3c3f41;cursor:pointer}.pf-map .pf-map-endpoint-source:hover circle,.pf-map .pf-map-endpoint-target:hover circle{stroke:#e28a0d !important}.pf-map .pf-map-endpoint-source.jsplumb-hover,.pf-map .pf-map-endpoint-target.jsplumb-hover{z-index:95}.pf-map .pf-map-endpoint-source.jsplumb-dragging circle,.pf-map .pf-map-endpoint-target.jsplumb-dragging circle{stroke:#e28a0d}.pf-map .jsplumb-endpoint-drop-allowed circle{stroke:#5cb85c !important;fill:#5cb85c !important}.pf-map .jsplumb-endpoint-drop-forbidden circle{stroke:#a52521 !important;fill:#a52521 !important}.pf-map svg.jsplumb-connector{cursor:pointer;stroke-linecap:round;-webkit-transition:stroke 0.18s ease-out;transition:stroke 0.18s ease-out;will-change:all}.pf-map svg.jsplumb-connector path{-webkit-transition:stroke 0.18s ease-out;transition:stroke 0.18s ease-out}.pf-map svg.jsplumb-connector path:nth-child(2){stroke:#3c3f41}.pf-map svg.jsplumb-connector path:first-child{stroke:#63676a}.pf-map svg.jsplumb-connector.jsplumb-hover{z-index:80}.pf-map svg.jsplumb-connector.jsplumb-hover path:first-child{stroke:#eaeaea}.pf-map svg.jsplumb-connector.jsplumb-dragging{-webkit-transition:opacity 0.18s ease-out;transition:opacity 0.18s ease-out;opacity:0.4;z-index:80}.pf-map svg.pf-map-connection-jumpbridge{z-index:50}.pf-map svg.pf-map-connection-jumpbridge path:first-child{stroke:rgba(255,255,255,0)}.pf-map svg.pf-map-connection-jumpbridge path:nth-child(2){stroke:#568a89}.pf-map svg.pf-map-connection-jumpbridge:hover path:first-child{stroke:rgba(255,255,255,0)}.pf-map svg.pf-map-connection-jumpbridge:hover path:nth-child(2){stroke:#eaeaea}.pf-map svg.pf-map-connection-stargate{z-index:60}.pf-map svg.pf-map-connection-stargate path:first-child{stroke:#63676a}.pf-map svg.pf-map-connection-stargate path:nth-child(2){stroke:#313966}.pf-map svg.pf-map-connection-stargate:hover path:first-child{stroke:#eaeaea}.pf-map svg.pf-map-connection-wh-fresh,.pf-map svg.pf-map-connection-wh-reduced,.pf-map svg.pf-map-connection-wh-critical,.pf-map svg.pf-map-connection-wh-eol{z-index:70}.pf-map svg.pf-map-connection-wh-eol path:first-child{stroke:#d747d6}.pf-map svg.pf-map-connection-wh-eol:hover path:first-child{stroke:#eaeaea}.pf-map svg.pf-map-connection-wh-reduced path:nth-child(2){stroke:#e28a0d}.pf-map svg.pf-map-connection-wh-critical path:nth-child(2){stroke:#a52521}.pf-map .pf-map-connection-overlay{padding:1px 4px;font-size:11px;z-index:1020;background-color:#3c3f41;color:#adadad;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.pf-map .frig{background-color:#f0ad4e;color:#1d1d1d}.pf-map .mass{background-color:#a52521;color:#eaeaea}.pf-map .eol{background-color:#3c3f41;color:#d747d6}.pf-map .pf-map-connection-arrow-overlay{stroke:#313335;fill:#5cb85c}.pf-map .pf-map-connection-diamond-overlay{stroke:#313335;fill:#d9534f;animation-name:pfPulseDanger;animation-duration:4s;animation-iteration-count:infinite}.pf-map .pf-map-connection-small-overlay{font-family:Arial, sans-serif;padding:0 2px;font-size:10px;z-index:1020;background-color:#3c3f41;color:#adadad;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.ui-dialog-content label{min-width:60px}.dropdown-menu{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;z-index:1020;will-change:opacity, top, left, transform}.dropdown-menu a{cursor:pointer}.dropdown-menu i{width:20px}.pf-system-tooltip-inner{color:#adadad;padding:2px 4px;min-width:25px;-webkit-transition:color 0.2s ease-out;transition:color 0.2s ease-out}.pf-system-info-module h5{text-transform:capitalize;line-height:16px}.pf-system-info-module .pf-system-info-description-area{min-height:123px}.pf-system-info-module .pf-system-info-description-area .editable-container{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group .editable-input{width:calc(100% - 75px)}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group .editable-input textarea{width:100%;max-height:200px;resize:vertical}.pf-system-info-module .pf-system-info-description-area .pf-form-field-char-count{display:block;margin-top:10px}.pf-system-info-module .pf-system-info-table{font-size:11px;white-space:nowrap}.pf-sig-table-module .pf-sig-table-clear-button{will-change:opacity, transform;display:none}.pf-sig-table-module .pf-sig-table{font-size:10px}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text{white-space:normal}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty{border-bottom:none}.pf-sig-table-module .pf-sig-table .pf-editable-description{background-color:#2b2b2b;max-height:50px}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-name-input{text-transform:uppercase}.pf-sig-table-module .pf-sig-table .pf-editable-filter{color:#63676a;border:none;font-style:normal}.pf-editable-filter-active{min-width:100px}.pf-system-graph-module .pf-system-graph{width:100%;height:100px}.pf-system-route-module .pf-system-route-table{width:100%;font-size:10px}.pf-system-route-module .pf-system-route-table td{text-transform:capitalize}.pf-system-route-module .pf-system-route-table td>.fa{font-size:10px}.pf-system-killboard-module .pf-system-killboard-graph-kills{width:100%;height:100px;position:relative;margin-bottom:30px}.pf-system-killboard-module .pf-system-killboard-list{padding-bottom:10px;border-bottom:1px solid #2b2b2b}.pf-system-killboard-module .pf-system-killboard-list li{margin-left:0;overflow:visible;min-height:50px;will-change:margin-left;-webkit-transition:margin-left 0.12s cubic-bezier(0.3, 0.8, 0.8, 1.7);transition:margin-left 0.12s cubic-bezier(0.3, 0.8, 0.8, 1.7)}.pf-system-killboard-module .pf-system-killboard-list li h5{white-space:nowrap}.pf-system-killboard-module .pf-system-killboard-list li h3{width:120px;display:inline-block}.pf-system-killboard-module .pf-system-killboard-list li .pf-system-killboard-img-corp{margin-right:10px;width:16px}.pf-system-killboard-module .pf-system-killboard-list li .pf-system-killboard-img-ship{width:50px;margin-right:10px;border:1px solid #2b2b2b;transform:translateZ(1px);will-change:border-color;-moz-border-radius:25px;-webkit-border-radius:25px;border-radius:25px;-webkit-transition:border-color 0.12s ease-out;transition:border-color 0.12s ease-out}.pf-system-killboard-module .pf-system-killboard-list li:before{content:"\f054";font-family:FontAwesome;position:absolute;z-index:10;left:-25px;top:15px;color:#477372;opacity:0;will-change:opacity, left;-webkit-transition:all 0.12s ease-out;transition:all 0.12s ease-out}.pf-system-killboard-module .pf-system-killboard-list li:hover{margin-left:20px}.pf-system-killboard-module .pf-system-killboard-list li:hover .pf-system-killboard-img-ship{border-color:#568a89}.pf-system-killboard-module .pf-system-killboard-list li:hover:before{opacity:1;left:-20px}input,select{background-color:#313335;color:#adadad;border:1px solid #63676a;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}input:focus,select:focus{border-color:#568a89}input:-webkit-autofill,select:-webkit-autofill{background-color:#313335 !important;-webkit-box-shadow:0 0 0 50px #313335 inset !important;box-shadow:0 0 0 50px #313335 inset !important;-webkit-text-fill-color:#adadad}input:-webkit-autofill:focus,select:-webkit-autofill:focus{-webkit-box-shadow:0 0 0 50px #313335 inset !important;box-shadow:0 0 0 50px #313335 inset !important;-webkit-text-fill-color:#adadad}input::-webkit-file-upload-button,select::-webkit-file-upload-button{background-color:transparent;border:none;color:#63676a;outline:none}.btn-fake{border:none;text-align:left;cursor:default;opacity:1 !important;color:#63676a !important;background-color:#3c3f41 !important}.pf-form-dropzone{border:2px dashed #2b2b2b;height:100px;background-color:#353739;text-align:center;font-size:20px;line-height:100px;margin:15px 0;color:#2b2b2b;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;-webkit-transition:color 0.18s ease-out,border-color 0.18s ease-out;transition:color 0.18s ease-out,border-color 0.18s ease-out}.pf-form-dropzone:hover{color:#568a89;border-color:#568a89;cursor:-moz-grabbing;cursor:-webkit-grabbing;cursor:grabbing}.toggle.btn:active{box-shadow:none}.toggle .toggle-group .btn{padding:0px 5px}.pf-icon{display:inline-block}.pf-icon.disabled{opacity:0.5;color:#63676a}.pf-icon-dotlan{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAwpJREFUeNqslE9oXFUUxr9z7333vZk3k3+1JGkyldI44KYKFbSIihbauBOrNkgXdeGqVnAhFnfiQgTFCtJNEQndVDDSFrRBWiGutEYFi2mlhQraP2km02by8ubdd/8cF8WJi+z0bA/nx3cO5/uImfFfSwHAwh9PbNR7EMBLBGoyuA3gKwCzAEAEbLu/gqkXLuLUFzchNhjeAuAXSWJBkXyFwcOSxOOK5FkCMYDnN1QSAoOIQIQnBdGcAP2a+3Kb8dbZYBuCZCcW0Y00io8IiBnP4QMCvSnEvyBbRxMstsuGKXiOJD5ZLlcP586cFJr3S025Z1vtlmY5s90HhpL6dKKi30wIf+W5/xjAvXWmTyzuyLJwvr8eXejYtcOZ6/4ZV2g/Z/rZsqVTu6wpEvJcUK7dLlYDI7zsLB+VUj3cU/L1mSX9yM6+YzzsjmeuOJHEary7pOjulURzoNPs8VN11E4NNnnROHdppcxHtsjKwSRWsgeJRDzvOMyvlMUmpXHAZvLVO5criiQPqzgMMeOd7LpuRqk/UBsPr+fGHOw68z4xobfO9ocIHXSQGzuhpECwdArAcRHxRQbWQAAJ3mdzqYnoZwC7looOCl+uQ9q3gIg1pKTbITCE4jEA37GjfhD2AgA7WpBRKAE0AviaFhKSxDqkdcuM9Im0OZCk10wRrug+/2H/RPGZL+mYzSTKVfl7bax8rD5unzEFb45IfTqcDqbBYqx3k92Tm8bjKmZil3xbkfGurjGt2hh/RIRDZkUdkjqg3jA7g+DzocRbg2maO8vZ5hG9F8B1YmbcyXfjdttO+hJnhcBky6zMd71t6YS8lDjHAROl4e0I9PaArr03OlQzP1y4Ozc0GO156tHv7ym5sViCCLMQeINIzN6XDBzJbFcYa/e5MjQF0Zm6VDPVOK5pEfHqmr3a2JrsSVOx/rFEPRsc9RxuEuhkf1R916sw7REuC9CIJPlNAO9w7E+zw3P1usQ/CaA2MODnDP7Ssn+NQC8KiKcBFI79jwCmAFwiArxfjxD6P/Lk7wEA9Dls2LsiUxoAAAAASUVORK5CYII=');width:17px;height:17px;opacity:0.8;margin:-5px 0px 0 10px}.pf-icon-wormhol-es{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA5xJREFUeNp8lMtrXGUAxc/33e++Z+ZO5pHJZJK0STSx6UMqYrFYqF1I42NRFxVK3dhF3QiCLkQXbrpzoeBOi0iRbHQhWqEIohJRtAmW1tKaZiZSJ4+ZZObOnZl779zHdz8XkYJIe/6Aw+Fwfoc8e3IR95MggBpJCBk/vJ7rPy3HUlONaT89UDa1kC0FMudEAAwPkJQQyJzmlqYabw3UKGO6ahM0UdtGWDlQL3w8ZpuX+mr0XxMCQNxLIZD2FFydbL7TV2Jj/tres6HCB5QT9eZY69yfZfulim1epkCb3osdS5oSSWU1liwjZLTQM1DP9V/csrxDR6qlCwM1bvly7Ppq3N6/nv+IcRKsljpn9JCBCSJg+Qpqhd752oj9nDpQtghBONzVq77Op7K+Uiv29V93Uj6oIOBUgHHijNupK9Wic3q6aX1OjZChZQYnboy1To2101+XHePKiGMs3y7Zj0oJZRqXjWqh83wqkEEAUAFwKvStjP/4kKutEMBlRJDS8p7t1yda6cWDdwsfemoEPWTIeMrt1RHnVStUozsjzgtlJ3WLcepkPMX+fc/Oa7YRzMzfmDgzkHmfXa+0zxNB4sfuFi+0UgMAgKdwDPf079fy3WMRFftKrsl+e2jzDRZLAhCinQqmn1wrvQdgg1MBaoRsiyVEhIyb9/YBAZZQUAE3YIl1sFHcLPqmXwgML8t1cbQ2+mmup37jqhGIAOhsw1pIiKArw51Xij0dWizB8hXUs/0Tns6fKnf12rcPr031lCDnqIN82/BKm5a7TwCMgOzuaW7qXEgF1W9W2qclQHPUeDKS+eS1idbZyY7V2cj2CiVb/+7A34UPTF9etlx15Xpl52UjkAc5T/sjlhIwV4kxahtfekp2ej3XP55wiSdypI130k1XDSkLSXX/Rv6Trh5yJmg772h3xtuZY7VS99S4nfoKMjwyf3IRBIAZMCQEOpcSE4JIXBJzP83U336iWn5XD9nPAeMgAIgAtIiN/jhX/2xmc+jisGMsUPJvma4aw1diP5KSnVhKGlpIfzAHytZf+d4zWiTtIkEEjFBGM+MfjYlAypfXEiJA/0cugIQIcCrE3m3ri3quf7yvRofTA1nKuhpimjyyNNl4c3ZzaCHrqb9EjN+fYl/hqNjm5UbWnV+cq78/1DVWFU7c9Zx7aKKdujrdtC7aZgAiCMiD/kRKCADMbmf8IwPG9UiOs5RTMtW0LiVUbHC6y/w/AwBZTrZC1ec/kgAAAABJRU5ErkJggg==');width:17px;height:17px;opacity:0.8;margin:-5px 0px 0 10px}.modal-content h2{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;letter-spacing:0px;font-size:14px;margin:20px 0;line-height:normal}.modal-content h2.pf-dynamic-area,.modal-content h4.pf-dynamic-area{min-height:0;margin:10px 0}.modal-content h2.pf-dynamic-area>img,.modal-content h4.pf-dynamic-area>img{margin:-10px 5px -10px -10px;width:35px}.modal-content .dataTable,.modal-content .table{font-size:10px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.modal-content hr{margin:5px 0 15px 0;border-color:#63676a}.modal-content .pf-wizard-navigation{margin:0}.modal-content .pf-wizard-navigation li:not(:last-child):before{border-top:1px solid #63676a;content:"";display:block;font-size:0;overflow:hidden;position:relative;top:12px;left:71px;right:1px;width:100%}.modal-content .pf-wizard-navigation li.finished:before{-moz-border-image:-moz-linear-gradient(left, #375959,#375959) 1 1%;-moz-border-image:linear-gradient(to right, #375959,#375959) 1 1%;-o-border-image:linear-gradient(to right, #375959,#375959) 1 1%;-webkit-border-image:-webkit-linear-gradient(left, #375959,#375959) 1 1%;-webkit-border-image:linear-gradient(to right, #375959,#375959) 1 1%;border-image:-moz-linear-gradient(left, #375959,#375959) 1 1%;border-image:-webkit-linear-gradient(left, #375959,#375959) 1 1%;border-image:linear-gradient(to right, #375959,#375959) 1 1%;border-bottom:0}.modal-content .pf-wizard-navigation li.active:before{-moz-border-image:-moz-linear-gradient(left, #4f9e4f,#63676a) 1 1%;-moz-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;-o-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;-webkit-border-image:-webkit-linear-gradient(left, #4f9e4f,#63676a) 1 1%;-webkit-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;border-image:-moz-linear-gradient(left, #4f9e4f,#63676a) 1 1%;border-image:-webkit-linear-gradient(left, #4f9e4f,#63676a) 1 1%;border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;border-bottom:0}.modal-content .pf-wizard-navigation li>h6{color:#63676a;font-size:11px;margin:5px}.modal-content .pf-wizard-navigation li a:hover+h6{color:#adadad}.modal-content .pf-wizard-navigation li.active a:not(.btn-danger)+h6{color:#adadad}#pf-settings-dialog .form-group .btn-sm,#pf-settings-dialog .form-group .btn-group-sm>.btn{padding:4px 7px 3px}#pf-settings-dialog #pf-dialog-captcha-wrapper{margin:0;padding:3px 0}#pf-map-dialog #pf-map-dialog-character-select,#pf-map-dialog #pf-map-dialog-corporation-select,#pf-map-dialog #pf-map-dialog-alliance-select{width:300px}#pf-route-dialog #pf-route-dialog-map-select{width:300px !important}#pf-shortcuts-dialog td kbd:last-of-type+i{display:none}#pf-manual-scrollspy{position:relative;height:700px;overflow:auto}.pf-system-dialog-select{width:270px !important}#pf-task-dialog .pf-task-dialog-status{min-height:auto}.pf-credits-dialog .pf-credits-logo-background{overflow:visible;background:url("../img/logo_bg.png");background-size:cover;padding:20px;margin-bottom:20px}.pf-credits-dialog #pf-logo-container{width:355px;height:366px;margin:0 auto}.pf-credits-dialog .pf-dynamic-area{min-height:50px}.pf-credits-dialog .dl-horizontal{display:inline-block;width:48%}.pf-credits-dialog .btn{padding:0}.pf-credits-dialog blockquote{font-size:14px}.pf-log-graph{height:100px;width:100%}.pf-animation-slide-in{-moz-animation-duration:1.2s;-webkit-animation-duration:1.2s;-moz-animation-name:pfSlideIn;-webkit-animation-name:pfSlideIn;position:relative}@-webkit-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-moz-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-ms-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-webkit-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@-moz-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@-ms-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}.pf-animation-pulse-success{-webkit-animation:pulseBackgroundSuccess 1.5s 1;animation:pulseBackgroundSuccess 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-success .sorting_1{-webkit-animation:pulseBackgroundSuccessActive 1.5s 1;animation:pulseBackgroundSuccessActive 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-warning{-webkit-animation:pulseBackgroundWarning 1.5s 1;animation:pulseBackgroundWarning 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-warning .sorting_1{-webkit-animation:pulseBackgroundWarningActive 1.5s 1;animation:pulseBackgroundWarningActive 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}@-webkit-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-moz-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-ms-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-webkit-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-moz-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-ms-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-webkit-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-moz-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-ms-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-webkit-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@-moz-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@-ms-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}.pf-animate-rotate{-webkit-transition:all 0.08s linear;transition:all 0.08s linear}.pf-animate-rotate.right{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.timeline{list-style:none;position:relative}.timeline:before{top:0;bottom:0;position:absolute;content:" ";width:1px;left:50%;margin-top:20px;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzRmOWU0ZiIvPjxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNvbG9yPSIjNjM2NzZhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4f9e4f),color-stop(25%, #63676a));background-image:-moz-linear-gradient(top, #4f9e4f,#63676a 25%);background-image:-webkit-linear-gradient(top, #4f9e4f,#63676a 25%);background-image:linear-gradient(to bottom, #4f9e4f,#63676a 25%)}.timeline>li{margin-bottom:20px;position:relative}.timeline>li.timeline-first .timeline-title{color:#4f9e4f}.timeline>li.timeline-first .timeline-badge{background-color:#4f9e4f}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{width:47%;float:left;border:1px solid #313335;padding:8px;position:relative;background-color:#313335;font-size:11px;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.timeline>li>.timeline-panel:before{content:" ";position:absolute;top:10px;right:-8px;display:inline-block;border-top:7px solid transparent;border-left:7px solid #63676a;border-right:0 solid #63676a;border-bottom:7px solid transparent}.timeline>li>.timeline-panel:after{content:" ";position:absolute;top:10px;right:-8px;display:inline-block;border-top:7px solid transparent;border-left:7px solid #63676a;border-right:0 solid #63676a;border-bottom:7px solid transparent}.timeline>li>.timeline-badge{color:#2b2b2b;width:22px;height:22px;line-height:22px;text-align:center;position:absolute;top:7px;left:50%;margin-left:-11px;background-color:#63676a;z-index:100;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{border-left-width:0;border-right-width:7px;left:-8px;right:auto}.timeline>li.timeline-inverted>.timeline-panel:after{border-left-width:0;border-right-width:8px;left:-9px;right:auto}.timeline-title{margin-top:0;color:inherit}.timeline-body>hr{display:none}.timeline-body>hr ~ *{display:none}.timeline-body>p,.timeline-body>ul{margin-bottom:0;list-style-type:disc;margin-left:15px}.timeline-body>p+p{margin-top:5px}@media (max-width: 1200px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 62px)}ul.timeline>li>.timeline-badge{left:29px;margin-left:0;top:6px}ul.timeline>li>.timeline-panel{float:right}ul.timeline>li>.timeline-panel:before{border-left-width:0;border-right-width:7px;left:-8px;right:auto}ul.timeline>li>.timeline-panel:after{border-left-width:0;border-right-width:7px;left:-8px;right:auto}}.ribbon-wrapper{width:72px;height:88px;overflow:hidden;position:absolute;top:-3px;right:-3px;pointer-events:none}.ribbon{font:bold 12px "Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;color:#2b2b2b;text-align:center;text-shadow:rgba(255,255,255,0.2) 0px 1px 0px;position:relative;padding:3px 0;left:-4px;top:16px;width:99px;-webkit-box-shadow:2px 3px 3px rgba(0,0,0,0.2);box-shadow:2px 3px 3px rgba(0,0,0,0.2);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ribbon:before,.ribbon:after{content:"";border-left:3px solid transparent;border-right:3px solid transparent;position:absolute;bottom:-3px}.ribbon.ribbon-default{color:#adadad;background-color:#353739;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzJkMzAzMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzJhMmIyZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2d3031),color-stop(100%, #2a2b2d));background-image:-moz-linear-gradient(top, #2d3031,#2a2b2d);background-image:-webkit-linear-gradient(top, #2d3031,#2a2b2d);background-image:linear-gradient(to bottom, #2d3031,#2a2b2d)}.ribbon.ribbon-default:before,.ribbon.ribbon-default:after{border-top:3px solid #000}.ribbon.ribbon-green{background-color:#5cb85c;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzUxYjM1MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRhOTQ0YSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #51b351),color-stop(100%, #4a944a));background-image:-moz-linear-gradient(top, #51b351,#4a944a);background-image:-webkit-linear-gradient(top, #51b351,#4a944a);background-image:linear-gradient(to bottom, #51b351,#4a944a)}.ribbon.ribbon-green:before,.ribbon.ribbon-green:after{border-top:3px solid #285028}.ribbon.ribbon-orange{background-color:#e28a0d;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Q0ODEwYyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2I0NmQwYiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #d4810c),color-stop(100%, #b46d0b));background-image:-moz-linear-gradient(top, #d4810c,#b46d0b);background-image:-webkit-linear-gradient(top, #d4810c,#b46d0b);background-image:linear-gradient(to bottom, #d4810c,#b46d0b)}.ribbon.ribbon-orange:before,.ribbon.ribbon-orange:after{border-top:3px solid #6c4107}.ribbon.ribbon-red{background-color:#d9534f;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2M5MzAyYyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2E4MjgyNCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c9302c),color-stop(100%, #a82824));background-image:-moz-linear-gradient(top, #c9302c,#a82824);background-image:-webkit-linear-gradient(top, #c9302c,#a82824);background-image:linear-gradient(to bottom, #c9302c,#a82824)}.ribbon.ribbon-red:before,.ribbon.ribbon-red:after{border-top:3px solid #541412}.ribbon.ribbon-blue{background-color:#428bca;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzM3ODRjNSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzJkNWM4NSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #3784c5),color-stop(100%, #2d5c85));background-image:-moz-linear-gradient(top, #3784c5,#2d5c85);background-image:-webkit-linear-gradient(top, #3784c5,#2d5c85);background-image:linear-gradient(to bottom, #3784c5,#2d5c85)}.ribbon.ribbon-blue:before,.ribbon.ribbon-blue:after{border-top:3px solid #1a344c}.ribbon:before{left:0}.ribbon:after{right:0}.pf-loading-bars-container{position:relative;z-index:4;margin:0 auto;left:5px;right:19px;width:70px;height:50px;list-style:none}.pf-loading-bars-container .pf-loading-bars-loader{position:absolute;z-index:3;margin:0 auto;left:0;right:0;top:50%;margin-top:-19px;width:56px;height:37px;list-style:none}.pf-loading-bars-container .pf-loading-bars-loader li{background-color:#5cb85c;width:6px;height:6px;float:right;margin-right:3px !important;-webkit-box-shadow:0px 12px 6px rgba(0,0,0,0.2);box-shadow:0px 12px 6px rgba(0,0,0,0.2)}.pf-loading-bars-container .pf-loading-bars-loader li:first-child{-webkit-animation:cssload-loadbars 1.75s cubic-bezier(0.645, 0.045, 0.355, 1) infinite 0s;animation:cssload-loadbars 1.75s cubic-bezier(0.645, 0.045, 0.355, 1) infinite 0s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(2){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -0.35s;animation:cssload-loadbars 1.75s ease-in-out infinite -0.35s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(3){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -0.7s;animation:cssload-loadbars 1.75s ease-in-out infinite -0.7s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(4){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.05s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.05s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(5){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.4s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.4s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(6){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.75s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.75s}@-webkit-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@-moz-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@-ms-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}.pf-landing-sticky-panel{position:fixed;min-width:100px;border-radius:5px;padding:7px;box-shadow:0 4px 10px rgba(0,0,0,0.4);background-color:rgba(43,43,43,0.7)}.pf-landing-sticky-panel h4{margin:5px 0 10px 0}.pf-landing-sticky-panel ul{margin-bottom:0}.pf-landing-sticky-panel ul li{text-transform:lowercase}#pf-landing-server-panel{top:50px;left:10px}#pf-landing-admin-panel{bottom:10px;right:10px}.youtube{background-position:center;background-repeat:no-repeat;position:relative;display:inline-block;overflow:hidden;transition:all 200ms ease-out;cursor:pointer}.youtube .play{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAERklEQVR4nOWbTWhcVRTHb1IJVoxGtNCNdal2JYJReC6GWuO83PM/59yUS3FRFARdFlwYP1CfiojQWt36sRCUurRIdVFXIn41lAoVdRGrG1M01YpKrWjiYmaSl8ybZJL3cd+YA//NLObd3++eO8x79z5jSq5Gw+8kov0AP8vMR5l1BtBZQM4B8ks75wCdZdYZZj5qLZ4hov2Nht9Z9vhKKSIaB/gI4M4w62KeAO6Mte4lYOq20FxrlqqOibhHmeWbvNC9ZfDX1mLae391aN6limO/gwgvAPJbWeAZuSDingdwXTBw7/0IsyaA/Fkh+KqOkD+YNfHej1QKD+y7iVlOhgLvFqFfNJvNGyuBJ+KDAF8MDd0tgS8y64OlgSdJMsysL4cG7SOHkyQZLhTee7+d2R2rAVy/S+Jd7/32ouBHAP4gNNRGQyTHc/84NhqNywZp5rvjjnnvt21aABFeCQ+RLwAf2hQ8s7sv9OCLk6AHNgQvIrvbfzKCD76g/O6cu7lf/iER/aQGgy448pExZmhdegAPhR9sObFWH1gT3lp7DaA/5bkIgJhZPgsNmz02novj+KqeApj1ubwXWe4kdyeznAgNvTpE/HQmvKqOMeuFogTUVQSRno+iaLRLAJF7uIgL9O4ubgL8aWgB7S44mNX+35YpICUiAvS9sBLkq1WzT+NFffl6AuoiApi6NT37h6sWkBIRZGkQ8YtLgyji6e1mBYTqCEBPG2Naz+0BWQgtoGoRgCzEsd9hAN1X5BfnFZASUfrSAFQNsyZ1FJASUVpHiLinDJG8U2cBZYogkrcNs5waBAGdstbeU9zdqpw0gPwwSAI6VUxHyFlDpOcHUUBBIuYNs14aZAE5RVwyzPr3/0EAEY0TyfGNjBWQvwZ +CTSbehfAH29mrID8bET0+0EUkAd8WYDOmqJ3ecsG30yr9wqRfm6Y+a1BEFDEjHfHvWmY9ck6CygHvBVr8Xhtb4ZE5HZA3y8DvBNA1TjnrmXWf+sioMwZX5V/VHXMGGMMoKdDCxCRvRWBdzKzdHEO+EisilbPyopHYqp6S9UCAsz4iojI7hUDAtyXVQgIDd6KnOoaWNkbI6FaPSuZGyMArsi7MZoloB4zviI/Nhr3X95jltwTRQmoIfgisy5ai+me67OI7fE4nrqjrqfK1t0eby0FPRB6oGVlchL3rgnfrq19RKbVBdhV9IOSwJmfmJi4vi/4ThERitwyCxVAFqydshuCX5awhQ9KtmuIWd8IDZED/nXT77rvVVv6sHRKwjYi91poqP7Dr+Y6JJ1VSZIMA3wkPNy6bX+o8Bcm0sXMdwM8Fxo0A3xORPaWBp6uPXsmbxCRD0NDL0dOANhVCXy6iAjMcjbcrMt3RITKwdMVRdFo+y5yvkL4eWZ+zHt/ZVD4dEVRNGotpst+dZZZH8k86lqn2pIvT/eqrNfn2xuyqYPZ8mv7s8pfn/8Pybm4TIjanscAAAAASUVORK5CYII=") no-repeat center center;background-size:64px 64px;position:absolute;height:100%;width:100%;opacity:.8;filter:alpha(opacity=80);transition:all 0.2s ease-out}.youtube .play:hover{opacity:1;filter:alpha(opacity=100)} + * ======================================================================== */label.checkbox .toggle,label.checkbox.inline .toggle{margin-left:-20px;margin-right:5px}.toggle{min-width:40px;height:20px;position:relative;overflow:hidden}.toggle input[type="checkbox"]{display:none}.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left 0.35s;-webkit-transition:left 0.35s;-moz-user-select:none;-webkit-user-select:none}.toggle.off .toggle-group{left:-100%}.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}.toggle-handle{position:relative;margin:0 auto;padding-top:0px;padding-bottom:0px;height:100%;width:0px;border-width:0 1px}.toggle-handle.btn-mini{top:-2px}.toggle.btn{min-width:30px}.toggle-on.btn{padding-right:24px}.toggle-off.btn{padding-left:24px}.toggle.btn-large{min-width:40px}.toggle-on.btn-large{padding-right:35px}.toggle-off.btn-large{padding-left:35px}.toggle.btn-small{min-width:25px}.toggle-on.btn-small{padding-right:20px}.toggle-off.btn-small{padding-left:20px}.toggle.btn-mini{min-width:20px}.toggle-on.btn-mini{padding-right:12px}.toggle-off.btn-mini{padding-left:12px}.checkbox{padding-left:20px}.checkbox label{display:inline-block;vertical-align:middle;position:relative;padding-left:5px}.checkbox label::before{content:"";display:inline-block;position:absolute;width:17px;height:17px;left:0;margin-left:-20px;border:1px solid #63676a;border-radius:3px;background-color:#313335;-webkit-transition:border 0.15s ease-in-out,color 0.15s ease-in-out;transition:border 0.15s ease-in-out,color 0.15s ease-in-out}.checkbox label::after{font-family:"FontAwesome";content:"";display:inline-block;position:absolute;width:16px;height:16px;left:0;top:0;opacity:0;transform:scale(2) rotateZ(-20deg);transition:all .18s ease-in;will-change:transform, opacity;margin-left:-20px;padding-left:3px;padding-top:1px;font-size:11px;color:#adadad}.checkbox input[type="checkbox"],.checkbox input[type="radio"]{opacity:0;z-index:1}.checkbox input[type="checkbox"]:focus+label::before,.checkbox input[type="radio"]:focus+label::before{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.checkbox input[type="checkbox"]:checked+label::after,.checkbox input[type="radio"]:checked+label::after{transform:scale(1) rotateZ(0deg);opacity:1}.checkbox input[type="checkbox"]:indeterminate+label::after,.checkbox input[type="radio"]:indeterminate+label::after{display:block;content:"";width:10px;height:3px;background-color:#555555;border-radius:2px;margin-left:-16.5px;margin-top:7px}.checkbox input[type="checkbox"]:disabled+label,.checkbox input[type="radio"]:disabled+label{opacity:0.65}.checkbox input[type="checkbox"]:disabled+label::before,.checkbox input[type="radio"]:disabled+label::before{background-color:#3c3f41;cursor:not-allowed}.checkbox.checkbox-circle label::before{border-radius:50%}.checkbox.checkbox-inline{margin-top:0}.checkbox-primary input[type="checkbox"]:checked+label::before,.checkbox-primary input[type="radio"]:checked+label::before{background-color:#375959;border-color:#375959}.checkbox-primary input[type="checkbox"]:checked+label::after,.checkbox-primary input[type="radio"]:checked+label::after{color:#fff}.checkbox-danger input[type="checkbox"]:checked+label::before,.checkbox-danger input[type="radio"]:checked+label::before{background-color:#a52521;border-color:#a52521}.checkbox-danger input[type="checkbox"]:checked+label::after,.checkbox-danger input[type="radio"]:checked+label::after{color:#fff}.checkbox-info input[type="checkbox"]:checked+label::before,.checkbox-info input[type="radio"]:checked+label::before{background-color:#316490;border-color:#316490}.checkbox-info input[type="checkbox"]:checked+label::after,.checkbox-info input[type="radio"]:checked+label::after{color:#fff}.checkbox-warning input[type="checkbox"]:checked+label::before,.checkbox-warning input[type="radio"]:checked+label::before{background-color:#e28a0d;border-color:#e28a0d}.checkbox-warning input[type="checkbox"]:checked+label::after,.checkbox-warning input[type="radio"]:checked+label::after{color:#fff}.checkbox-success input[type="checkbox"]:checked+label::before,.checkbox-success input[type="radio"]:checked+label::before{background-color:#4f9e4f;border-color:#4f9e4f}.checkbox-success input[type="checkbox"]:checked+label::after,.checkbox-success input[type="radio"]:checked+label::after{color:#fff}.checkbox-primary input[type="checkbox"]:indeterminate+label::before,.checkbox-primary input[type="radio"]:indeterminate+label::before{background-color:#375959;border-color:#375959}.checkbox-primary input[type="checkbox"]:indeterminate+label::after,.checkbox-primary input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-danger input[type="checkbox"]:indeterminate+label::before,.checkbox-danger input[type="radio"]:indeterminate+label::before{background-color:#a52521;border-color:#a52521}.checkbox-danger input[type="checkbox"]:indeterminate+label::after,.checkbox-danger input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-info input[type="checkbox"]:indeterminate+label::before,.checkbox-info input[type="radio"]:indeterminate+label::before{background-color:#316490;border-color:#316490}.checkbox-info input[type="checkbox"]:indeterminate+label::after,.checkbox-info input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-warning input[type="checkbox"]:indeterminate+label::before,.checkbox-warning input[type="radio"]:indeterminate+label::before{background-color:#e28a0d;border-color:#e28a0d}.checkbox-warning input[type="checkbox"]:indeterminate+label::after,.checkbox-warning input[type="radio"]:indeterminate+label::after{background-color:#fff}.checkbox-success input[type="checkbox"]:indeterminate+label::before,.checkbox-success input[type="radio"]:indeterminate+label::before{background-color:#4f9e4f;border-color:#4f9e4f}.checkbox-success input[type="checkbox"]:indeterminate+label::after,.checkbox-success input[type="radio"]:indeterminate+label::after{background-color:#fff}.radio{padding-left:20px}.radio label{display:inline-block;vertical-align:middle;position:relative;padding-left:5px}.radio label::before{content:"";display:inline-block;position:absolute;width:17px;height:17px;left:0;margin-left:-20px;border:1px solid #63676a;border-radius:50%;background-color:#313335;-webkit-transition:border 0.15s ease-in-out;transition:border 0.15s ease-in-out}.radio label::after{display:inline-block;position:absolute;content:" ";width:11px;height:11px;left:3px;top:3px;margin-left:-20px;border-radius:50%;background-color:#adadad;-webkit-transform:scale(0,0);-ms-transform:scale(0,0);transform:scale(0,0);-webkit-transition:-webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);-moz-transition:-moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);-o-transition:-o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33);transition:transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33)}.radio input[type="radio"]{opacity:0;z-index:1}.radio input[type="radio"]:focus+label::before{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;outline-color:#568a89}.radio input[type="radio"]:checked+label::after{-webkit-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1)}.radio input[type="radio"]:disabled+label{opacity:0.65}.radio input[type="radio"]:disabled+label::before{cursor:not-allowed}.radio.radio-inline{margin-top:0}.radio-primary input[type="radio"]+label::after{background-color:#375959}.radio-primary input[type="radio"]:checked+label::before{border-color:#375959}.radio-primary input[type="radio"]:checked+label::after{background-color:#375959}.radio-danger input[type="radio"]+label::after{background-color:#a52521}.radio-danger input[type="radio"]:checked+label::before{border-color:#a52521}.radio-danger input[type="radio"]:checked+label::after{background-color:#a52521}.radio-info input[type="radio"]+label::after{background-color:#316490}.radio-info input[type="radio"]:checked+label::before{border-color:#316490}.radio-info input[type="radio"]:checked+label::after{background-color:#316490}.radio-warning input[type="radio"]+label::after{background-color:#e28a0d}.radio-warning input[type="radio"]:checked+label::before{border-color:#e28a0d}.radio-warning input[type="radio"]:checked+label::after{background-color:#e28a0d}.radio-success input[type="radio"]+label::after{background-color:#4f9e4f}.radio-success input[type="radio"]:checked+label::before{border-color:#4f9e4f}.radio-success input[type="radio"]:checked+label::after{background-color:#4f9e4f}input[type="checkbox"].styled:checked+label:after,input[type="radio"].styled:checked+label:after{font-family:"FontAwesome";content:""}input[type="checkbox"] .styled:checked+label::before,input[type="radio"] .styled:checked+label::before{color:#fff}input[type="checkbox"] .styled:checked+label::after,input[type="radio"] .styled:checked+label::after{color:#fff}html{margin:0;padding:0;height:100%;position:relative}body{margin:0;padding:0;min-height:100%;direction:ltr}body.mobile-view-activated.hidden-menu{overflow-x:hidden}body.modal-open{overflow:hidden !important}a:hover,a:active,a:focus,button,button:active,button:focus,object,embed,input::-moz-focus-inner{outline:0}h1,h3,h4{margin:0;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.page-title{margin:12px 0 28px}.page-title span{font-size:15px;color:#313335;display:inline-block;vertical-align:1px}label{font-weight:normal}*:focus{outline:0 !important}a,input,button{-ms-touch-action:none !important}textarea:focus,select:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{outline:0;outline:thin dotted \9;box-shadow:inset -1px 1px 5px 0 rgba(0,0,0,0.8) !important}.input-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn,.input-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn,.input-xs,.form-control{border-radius:0px !important;-webkit-border-radius:0px !important;-moz-border-radius:0px !important}.input-xs{height:24px;padding:2px 10px;font-size:11px;line-height:1.5}.btn-xs,.btn-group-xs>.btn{padding:0px 2px;font-size:10px;line-height:1.3}.btn-sm,.btn-group-sm>.btn{padding:5px 8px 4px}.btn-lg,.btn-group-lg>.btn{padding:10px 16px}.no-space{margin:0}.no-space>[class*="col-"]{margin:0 !important;padding-right:0;padding-left:0}h1{letter-spacing:-1px;font-size:22px;margin:10px 0}h1 small{font-size:12px;font-weight:300;letter-spacing:-1px}h2{font-size:20px;margin:20px 0;line-height:normal}h3{display:block;font-size:17px;font-weight:400;margin:20px 0;line-height:normal}h4{line-height:normal;margin:20px 0 10px 0}h5{font-size:14px;font-weight:300;margin-top:0;margin-bottom:10px;line-height:normal}h6{font-size:13px;margin:10px 0;font-weight:bold;line-height:normal}.row-seperator-header{margin:15px 14px 20px;border-bottom:none;display:block;color:#303133;font-size:20px;font-weight:400}.center-canvas,.center-child-canvas>canvas{display:block !important;margin:0 auto !important}.smart-accordion-default.panel-group{margin-bottom:0px}.smart-accordion-default.panel-group .panel+.panel{margin-top:-1px}.smart-accordion-default.panel-group .panel-heading{padding:0px}.smart-accordion-default.panel-group .panel-title a{display:block;padding:10px 15px;text-decoration:none !important}.smart-accordion-default .panel-heading,.panel-group .panel{border-radius:0px;-webkit-border-radius:0px;-moz-border-radius:0px}.smart-accordion-default .panel-default>.panel-heading{background-color:#f3f3f3}.smart-accordion-default .panel-default{border-color:#8d9194}.smart-accordion-default .panel-title>a>:first-child{display:none}.smart-accordion-default .panel-title>a.collapsed>.fa{display:none}.smart-accordion-default .panel-title>a.collapsed>:first-child{display:inline-block}.no-padding .smart-accordion-default>div{border-left:none !important;border-right:none !important}.no-padding .smart-accordion-default>div:first-child{border-top:none !important}.no-padding .smart-accordion-default>div:last-child{border-bottom:none !important}.onoffswitch-container{margin-top:4px;margin-left:7px;display:inline-block}.onoffswitch{position:relative;width:50px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;margin-top:3px;margin-bottom:3px;margin-left:5px;display:inline-block;vertical-align:middle}.onoffswitch-checkbox{display:none}.onoffswitch-label{display:block;overflow:hidden;cursor:pointer;border:1px solid #484c4e;border-radius:50px;border-color:#777b7f #7c8184 #686c6f;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.onoffswitch-inner{width:200%;margin-left:-100%;display:block}.onoffswitch-inner:before,.onoffswitch-inner:after{float:left;width:50%;height:15px;padding:0;line-height:15px;font-size:10px;color:#fff;font-family:Trebuchet, Arial, sans-serif;font-weight:bold;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.onoffswitch-inner:before{content:attr(data-swchon-text);text-shadow:0 -1px 0 #313335;padding-left:7px;background-color:#3276b1;color:#fff;box-shadow:inset 0 2px 6px rgba(0,0,0,0.5),0 1px 2px rgba(0,0,0,0.05);text-align:left}.onoffswitch-inner:after{content:attr(data-swchoff-text);padding-right:7px;text-shadow:0 -1px 0 #fff;background-color:#fff;color:#3c3f41;text-align:right;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.onoffswitch-switch{width:19px;height:19px;margin:-2px;background:white;border:1px solid #64686b;border-radius:50px;position:absolute;top:0;bottom:0;right:32px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;background-color:#eaeaea;background-image:-moz-linear-gradient(top, #fff, #adadad);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#adadad));background-image:-webkit-linear-gradient(top, #fff, #adadad);background-image:-o-linear-gradient(top, #fff, #adadad);background-image:linear-gradient(to bottom, #ffffff,#adadad);background-repeat:repeat-x;-webkit-box-shadow:1px 1px 4px 0px rgba(0,0,0,0.3);box-shadow:1px 1px 4px 0px rgba(0,0,0,0.3)}.onoffswitch-checkbox+.onoffswitch-label .onoffswitch-switch:before,.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch:before{content:"\f00d";color:#a52521;display:block;text-align:center;line-height:19px;font-size:10px;text-shadow:0 -1px 0 #fff;font-weight:bold;font-family:FontAwesome}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch:before{content:"\f00c";color:#428bca}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-left:0;display:block}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:0px}.onoffswitch-switch:hover{background-color:#adadad}.onoffswitch-switch:active{background-color:#adadad;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.onoffswitch-checkbox:disabled+.onoffswitch-label .onoffswitch-inner:after,.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-inner:before{text-shadow:0 1px 0 #fff;background:#bfbfbf;color:#313335}.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-switch,.onoffswitch-checkbox:disabled+.onoffswitch-label .onoffswitch-switch{background-color:#eaeaea;background-image:-moz-linear-gradient(top, #bfbfbf, #eaeaea);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#bfbfbf), to(#eaeaea));background-image:-webkit-linear-gradient(top, #bfbfbf, #eaeaea);background-image:-o-linear-gradient(top, #bfbfbf, #eaeaea);background-image:linear-gradient(to bottom, #bfbfbf,#eaeaea);box-shadow:none !important}.onoffswitch-checkbox:disabled+.onoffswitch-label,.onoffswitch-checkbox:checked:disabled+.onoffswitch-label .onoffswitch-label{border-color:#74797c #63676a #525558 !important}.onoffswitch-checkbox:checked+.onoffswitch-label{border-color:#3276b1 #2a6395 #255681}.onoffswitch+span,.onoffswitch-title{display:inline-block;vertical-align:middle;margin-top:-5px}.form-control{box-shadow:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important}.form hr{margin-left:-13px;margin-right:-13px;border-color:rgba(0,0,0,0.1);margin-top:20px;margin-bottom:20px}.form fieldset{display:block;border:none;background:rgba(255,255,255,0.9);position:relative}fieldset{position:relative}.form-actions{display:block;padding:13px 14px 15px;border-top:1px solid rgba(0,0,0,0.1);background:rgba(239,239,239,0.9);margin-top:25px;margin-left:-13px;margin-right:-13px;margin-bottom:-13px;text-align:right}.well .form-actions{margin-left:-19px;margin-right:-19px;margin-bottom:-19px}.well.well-lg .form-actions{margin-left:-24px;margin-right:-24px;margin-bottom:-24px}.well.well-sm .form-actions{margin-left:-9px;margin-right:-9px;margin-bottom:-9px}.popover-content .form-actions{margin:0 -14px -9px;border-radius:0 0 3px 3px;padding:9px 14px}.no-padding .form .form-actions{margin:0;display:block;padding:13px 14px 15px;border-top:1px solid rgba(0,0,0,0.1);background:rgba(248,248,248,0.9);text-align:right;margin-top:25px}.form header,legend{display:block;padding:8px 0;border-bottom:1px dashed rgba(0,0,0,0.2);background:#fff;font-size:16px;font-weight:300;color:#2b2b2b;margin:25px 0px 20px}.no-padding .form header{margin:25px 14px 0}.form header:first-child{margin-top:10px}legend{font-weight:400;margin-top:0px;background:none}.input-group-addon{padding:6px 10px;will-change:background-color, border-color;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;-webkit-transition:all ease-out 0.15s;transition:all ease-out 0.15s}.input-group-addon .fa,.input-group-addon .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .input-group-addon li>i{font-size:14px}.input-group-addon .fa-lg,.input-group-addon .fa-2x{font-size:2em}.input-group-addon .fa-3x,.input-group-addon .fa-4x,.input-group-addon .fa-5x{font-size:30px}input[type="text"]:focus+.input-group-addon,input[type="password"]:focus+.input-group-addon,input[type="email"]:focus+.input-group-addon{border-color:#568a89;color:#568a89}.has-warning input[type="text"],.has-warning input[type="text"]+.input-group-addon{border-color:#e28a0d}.has-warning input[type="text"]+.input-group-addon{background-color:#fbe3c0;color:#2b2b2b}.has-warning input[type="text"]:focus,.has-warning input[type="text"]:focus+.input-group-addon{border-color:#e28a0d}.has-warning input[type="text"]:focus+.input-group-addon{background-color:#e28a0d;color:#fff}.has-error .input-group-addon{border-color:#d9534f !important;background:#d9534f !important;color:#2b2b2b !important}.has-success .input-group-addon{border-color:#4f9e4f !important;background-color:#2b2b2b !important;color:#4f9e4f !important}.form fieldset .form-group:last-child,.form fieldset .form-group:last-child .note,.form .form-group:last-child,.form .form-group:last-child .note{margin-bottom:0}.note{margin-top:6px;padding:0 1px;font-size:11px;line-height:15px;color:#7c8184}.input-icon-right>i,.input-icon-left>i{position:absolute;right:10px;top:10px;font-size:12px;color:#63676a}.input-icon-left>i{right:auto;left:24px}.input-icon-right .form-control{padding-right:27px}.input-icon-left .form-control{padding-left:29px}input[type="text"].ui-autocomplete-loading,input[type="password"].ui-autocomplete-loading,input[type="datetime"].ui-autocomplete-loading,input[type="datetime-local"].ui-autocomplete-loading,input[type="date"].ui-autocomplete-loading,input[type="month"].ui-autocomplete-loading,input[type="time"].ui-autocomplete-loading,input[type="week"].ui-autocomplete-loading,input[type="number"].ui-autocomplete-loading,input[type="email"].ui-autocomplete-loading,input[type="url"].ui-autocomplete-loading,input[type="search"].ui-autocomplete-loading,input[type="tel"].ui-autocomplete-loading,input[type="color"].ui-autocomplete-loading{background-image:url("../img/select2-spinner.gif") !important;background-repeat:no-repeat;background-position:99% 50%;padding-right:27px}.input-group-addon .checkbox,.input-group-addon .radio{min-height:0px;margin-right:0px !important;padding-top:0}.input-group-addon label input[type="checkbox"].checkbox+span,.input-group-addon label input[type="radio"].radiobox+span,.input-group-addon label input[type="radio"].radiobox+span:before,.input-group-addon label input[type="checkbox"].checkbox+span:before{margin-right:0px}.input-group-addon .onoffswitch,.input-group-addon .onoffswitch-label{margin:0}.alert{margin-bottom:10px;margin-top:0px;padding:5px 15px 5px 34px;color:#675100;border-width:0px;border-left-width:3px;padding:10px}.alert .ui-pnotify-title{line-height:12px}.alert .ui-pnotify-text{font-size:10px}.alert .close{top:0px;right:-5px;line-height:20px}.alert-heading{font-weight:600}.alert-danger{border-color:#a52521;color:#2b2b2b;background:#f6d1d0;text-shadow:none}.alert-danger .ui-pnotify-icon{color:#a52521}.alert-warning{border-color:#e28a0d;color:#2b2b2b;background:#fdedd8}.alert-warning .ui-pnotify-icon{color:#e28a0d}.alert-success{border-color:#4f9e4f;color:#2b2b2b;background:#d1e8d1}.alert-success .ui-pnotify-icon{color:#4f9e4f}.alert-info{border-color:#316490;color:#2b2b2b;background:#abc9e2}.alert-info .ui-pnotify-icon{color:#316490}.progress-micro{height:2px !important;line-height:2px !important}.progress-xs{height:7px !important;line-height:7px !important}.progress-sm{height:14px !important;line-height:14px !important}.progress-lg{height:30px !important;line-height:30px !important}.progress .progress-bar{position:absolute;overflow:hidden;line-height:18px}.progress .progressbar-back-text{position:absolute;width:100%;height:100%;font-size:12px;line-height:20px;text-align:center}.progress .progressbar-front-text{display:block;width:100%;font-size:12px;line-height:20px;text-align:center}.progress.right .progress-bar{right:0}.progress.right .progressbar-front-text{position:absolute;right:0}.progress.vertical{width:25px;height:100%;min-height:150px;margin-right:20px;display:inline-block;margin-bottom:0px}.progress.wide-bar{width:40px}.progress.vertical.bottom{position:relative}.progress.vertical.bottom .progressbar-front-text{position:absolute;bottom:0}.progress.vertical .progress-bar{width:100%;height:0;-webkit-transition:height 0.6s ease;transition:height 0.6s ease}.progress.vertical.bottom .progress-bar{position:absolute;bottom:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{position:relative;margin-bottom:20px;overflow:hidden;height:18px;background:#adadad;-webkit-box-shadow:0 1px 0 transparent,0 0 0 1px #aeb1b3 inset;box-shadow:0 1px 0 transparent,0 0 0 1px #aeb1b3 inset;-moz-border-radius:0px;-webkit-border-radius:0px;border-radius:0px}.progress-bar{float:left;width:0;height:100%;font-size:11px;color:#fff;text-align:center;background-color:#428bca;font-weight:bold;text-shadow:0 -1px 0 rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 1s ease-in-out;transition:width 1s ease-in-out}.progress-striped .progress-bar{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-danger{background-color:#a52521}.progress-striped .progress-bar-danger{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-success{background-color:#4f9e4f}.progress-striped .progress-bar-success{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-warning{background-color:#e28a0d}.progress-striped .progress-bar-warning{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-bar-info{background-color:#316490}.progress-striped .progress-bar-info{background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255,255,255,0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,0.15)), color-stop(0.75, rgba(255,255,255,0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0))}.progress-info .bar,.progress .bar-info{background:#316490}.vertical-bars{padding:0;margin:0}.vertical-bars:after{content:"";display:block;height:0;clear:both}.vertical-bars li{padding:14px 0;width:25%;display:block;float:left;text-align:center}.vertical-bars li:first-child{border-left:none}.vertical-bars>li>.progress.vertical:first-child{margin-left:auto}.vertical-bars>li>.progress.vertical{margin:0 auto;float:none}.nav-tabs{border-bottom:none}.nav-tabs>li>a .badge{font-size:11px;padding:3px 5px 3px 5px;opacity:.5;margin-left:5px;min-width:17px;font-weight:normal}.tabs-left .nav-tabs>li>a .badge{margin-right:5px;margin-left:0px}.nav-tabs>li>a .label{display:inline-block;font-size:11px;margin-left:5px;opacity:.5}.nav-tabs>li>a{color:#adadad;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.nav-tabs>li>a:hover{color:#1d1d1d;border-color:transparent transparent #adadad transparent;margin-top:1px;border-top-width:0}.nav-tabs>li.active>a{background-color:#adadad;color:#2b2b2b;border-top-width:0px !important;margin-top:1px !important;font-weight:bold}.tabs-left .nav-tabs>li.active>a{-webkit-box-shadow:-2px 0 0 #428bca;-moz-box-shadow:-2px 0 0 #428bca;box-shadow:-2px 0 0 #428bca;border-top-width:1px !important;border-left:none !important;margin-left:1px !important}.tabs-left .nav-pills>li.active>a{border:none !important;box-shadow:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important}.tabs-right .nav-tabs>li.active>a{-webkit-box-shadow:2px 0 0 #428bca;-moz-box-shadow:2px 0 0 #428bca;box-shadow:2px 0 0 #428bca;border-top-width:1px !important;border-right:none !important;margin-right:1px !important}.tabs-below .nav-tabs>li.active>a{-webkit-box-shadow:0 2px 0 #428bca;-moz-box-shadow:0 2px 0 #428bca;box-shadow:0 2px 0 #428bca;border-bottom-width:0px !important;border-top:none !important;margin-top:0px !important}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #9b9b9b}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li,.tabs-left>.nav-pills>li,.tabs-right>.nav-pills>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a,.tabs-left>.nav-pills>li>a,.tabs-right>.nav-pills>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs,.tabs-left>.nav-pills{float:left;margin-right:19px;border-right:1px solid #9b9b9b}.tabs-left>.nav-pills{border-right:none}.tabs-left>.nav-tabs>li>a{margin-right:-1px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#adadad #949494 #adadad #adadad}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#949494 transparent #949494 #9b9b9b;*border-right-color:#fff}.tabs-left>.tab-content{margin-left:109px}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #9b9b9b}.tabs-right>.nav-tabs>li>a{margin-left:-1px}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#adadad #adadad #adadad #9b9b9b}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#9b9b9b #9b9b9b #9b9b9b transparent;*border-left-color:#fff}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #9b9b9b}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#9b9b9b;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #9b9b9b #9b9b9b #9b9b9b}.nav-tabs.bordered{background:#fff;border:1px solid #9b9b9b}.nav-tabs.bordered>:first-child a{border-left-width:0px !important}.nav-tabs.bordered+.tab-content{border:1px solid #9b9b9b;border-top:none}.tabs-pull-right.nav-tabs>li,.tabs-pull-right.nav-pills>li{float:right}.tabs-pull-right.nav-tabs>li:first-child>a,.tabs-pull-right.nav-pills>li:first-child>a{margin-right:1px}.tabs-pull-right.bordered.nav-tabs>li:first-child>a,.tabs-pull-right.bordered.nav-pills>li:first-child>a{border-left-width:1px !important;margin-right:0px;border-right-width:0px}.dropdown-menu-xs{min-width:37px}.dropdown-menu-xs>li>a{padding:3px 10px}.dropdown-menu-xs>li>a:hover i{color:#fff !important}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu:hover>a{background-color:#63676a;color:#1d1d1d}.dropdown-submenu:hover a:after{border-left-color:#5cb85c}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#2b2b2b;margin-top:5px;margin-right:-10px}.dropdown-submenu>a:hover:after{border-left-color:#adadad}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px}.pagination>li>a,.pagination>li>span{box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05);-moz-box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05);-webkit-box-shadow:inset 0 -2px 0 rgba(0,0,0,0.05)}.btn-default.disabled{color:#adadad}.btn{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;will-change:background-color, border-color;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-transition:all 0.18s ease-in-out;transition:all 0.18s ease-in-out}.btn.btn-ribbon{background-color:#707070;background-image:-moz-linear-gradient(top, #777, #666);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#777), to(#666));background-image:-webkit-linear-gradient(top, #777, #666);background-image:-o-linear-gradient(top, #777, #666);background-image:linear-gradient(to bottom, #777777,#666666);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff777777', endColorstr='#ff666666', GradientType=0);color:white;padding:0 5px;line-height:20px;vertical-align:middle;height:20px;display:block;border:none;float:left;margin:0 8px 0 0;cursor:pointer}.btn.btn-ribbon>i{font-size:111%}.ribbon-button-alignment{padding-top:10px;display:inline-block}.ribbon-button-alignment.pull-right>.btn.btn-ribbon{margin:0 0 0 8px}.panel-purple{border-color:#6e587a}.panel-purple>.panel-heading{color:#fff;background-color:#6e587a;border-color:#6e587a}.panel-greenLight{border-color:#71843f}.panel-greenLight>.panel-heading{color:#fff;background-color:#71843f;border-color:#71843f}.panel-greenDark{border-color:#496949}.panel-greenDark>.panel-heading{color:#fff;background-color:#496949;border-color:#496949}.panel-darken{border-color:#313335}.panel-darken>.panel-heading{color:#fff;background-color:#404040;border-color:#404040}.panel-pink{border-color:#e06fdf}.panel-pink>.panel-heading{color:#fff;background-color:#e06fdf;border-color:#e06fdf}.panel-green{border-color:#5cb85c}.panel-green>.panel-heading{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.panel-blueLight{border-color:#92a2a8}.panel-blueLight>.panel-heading{color:#fff;background-color:#92a2a8;border-color:#92a2a8}.panel-pinkDark{border-color:#a8829f}.panel-pinkDark>.panel-heading{color:#fff;background-color:#a8829f;border-color:#a8829f}.panel-redLight{border-color:#a65858}.panel-redLight>.panel-heading{color:#fff;background-color:#a65858;border-color:#a65858}.panel-red{border-color:#d9534f}.panel-red>.panel-heading{color:#fff;background-color:#d9534f;border-color:#d9534f}.panel-teal{border-color:#568a89}.panel-teal>.panel-heading{color:#fff;background-color:#568a89;border-color:#568a89}.panel-orange{border-color:#e28a0d}.panel-orange>.panel-heading{color:#fff;background-color:#e28a0d;border-color:#e28a0d}.panel-blueDark{border-color:#4c4f53}.panel-blueDark>.panel-heading{color:#fff;background-color:#4c4f53;border-color:#4c4f53}.panel-magenta{border-color:#6e3671}.panel-magenta>.panel-heading{color:#fff;background-color:#6e3671;border-color:#6e3671}.panel-blue{border-color:#428bca}.panel-blue>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-footer>.btn-block{border-radius:0px;-moz-border-radius:0px;-webkit-border-radius:0px;border-bottom:none;border-left:none;border-right:none}.btn-circle{width:30px;height:30px;text-align:center;padding:6px 0;font-size:12px;line-height:18px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%;-webkit-box-shadow:0 1px 6px 0 rgba(0,0,0,0.12),0 1px 6px 0 rgba(0,0,0,0.12);box-shadow:0 1px 6px 0 rgba(0,0,0,0.12),0 1px 6px 0 rgba(0,0,0,0.12)}.btn-circle.btn-sm,.btn-group-sm>.btn-circle.btn{width:22px;height:22px;padding:4px 0;font-size:12px;line-height:14px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-circle.btn-lg,.btn-group-lg>.btn-circle.btn{width:50px;height:50px;padding:10px 15px;font-size:18px;line-height:30px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 15px;font-size:24px;line-height:50px;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.btn-metro{margin:0 0 20px;padding-top:15px;padding-bottom:15px}.btn-metro>span{display:block;vertical-align:bottom;margin-top:10px;text-transform:uppercase}.btn-metro>span.label{position:absolute;top:0px;right:0px}.btn-label{position:relative;left:-8px;display:inline-block;padding:5px 8px;background:rgba(0,0,0,0.15);border-radius:3px 0 0 3px}.btn-labeled{padding-top:0;padding-bottom:0;padding-left:8px}.btn-link{box-shadow:none;-webkit-box-shadow:none;font-size:13px}.morris-hover.morris-default-style{border-radius:5px;padding:5px;color:#666;background:rgba(29,29,29,0.9);border:solid 2px #375959;font-family:'Oxygen Bold';font-size:10px;text-align:left;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold}.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap}.morris-hover{position:absolute;z-index:903}.fixed-page-footer .morris-hover{z-index:900}.txt-color.txt-color-blue,.txt-color-blue.pf-help-light,.pf-help-light:hover,.txt-color-blue.pf-help,.pf-help:hover,.txt-color.pf-help-default:hover,.dataTable td.pf-help-default.pf-table-link-cell:hover,.dataTable td.pf-table-link-cell.pf-help-light:hover,.dataTable td.pf-table-link-cell.pf-help:hover,.dataTable td.pf-table-action-cell>.pf-help-default.pf-table-action-icon-cell:hover,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help-light:hover,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help:hover,.pf-landing .pf-landing-list li>i.pf-help-default:hover,.pf-landing .pf-landing-list li>i.pf-help-light:hover,.pf-landing .pf-landing-list li>i.pf-help:hover,.dataTable td.txt-color-blue.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-blue.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blue{color:#428bca !important}.txt-color.txt-color-blueLight,.txt-color-blueLight.pf-help-light,.txt-color-blueLight.pf-help,.dataTable td.txt-color-blueLight.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-blueLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blueLight{color:#92a2a8 !important}.txt-color.txt-color-blueDark,.txt-color-blueDark.pf-help-light,.txt-color-blueDark.pf-help,.dataTable td.txt-color-blueDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-blueDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-blueDark{color:#4c4f53 !important}.txt-color.txt-color-grayLightest,.txt-color-grayLightest.pf-help-light,.txt-color-grayLightest.pf-help,.dataTable td.txt-color-grayLightest.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-grayLightest.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayLightest{color:#eaeaea !important}.txt-color.txt-color-grayLighter,.txt-color-grayLighter.pf-help-light,.txt-color-grayLighter.pf-help,.dataTable td.txt-color-grayLighter.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-grayLighter.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayLighter{color:#adadad !important}.txt-color.txt-color-grayLight,.pf-help-light,.txt-color-grayLight.pf-help,.dataTable td.txt-color-grayLight.pf-table-link-cell,.dataTable td.pf-table-link-cell.pf-help-light,.dataTable td.pf-table-action-cell>.txt-color-grayLight.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-help-light,.pf-landing .pf-landing-list li>i.txt-color-grayLight,.pf-landing .pf-landing-list li>i.pf-help-light{color:#63676a !important}.txt-color.txt-color-gray,.txt-color-gray.pf-help-light,.pf-help,.dataTable td.txt-color-gray.pf-table-link-cell,.dataTable td.pf-table-link-cell.pf-help,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-gray,.pf-landing .pf-landing-list li>i.pf-help{color:#3c3f41 !important}.txt-color.txt-color-grayDark,.txt-color-grayDark.pf-help-light,.txt-color-grayDark.pf-help,.dataTable td.txt-color-grayDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-grayDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-grayDark{color:#313335 !important}.txt-color.txt-color-greenLight,.txt-color-greenLight.pf-help-light,.txt-color-greenLight.pf-help,.dataTable td.txt-color-greenLight.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-greenLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-greenLight{color:#66c84f !important}.txt-color.txt-color-green,.txt-color-green.pf-help-light,.pf-help-light.pf-log-info,.txt-color-green.pf-help,.pf-help.pf-log-info,.dataTable td.txt-color-green.pf-table-link-cell,.dataTable td.pf-table-link-cell.pf-log-info,.dataTable td.pf-table-action-cell>.txt-color-green.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-info,.txt-color.pf-log-info,.pf-landing .pf-landing-list li>i.pf-log-info,.pf-landing .pf-landing-list li>i.txt-color-green{color:#5cb85c !important}.txt-color.txt-color-greenDark,.txt-color-greenDark.pf-help-light,.txt-color-greenDark.pf-help,.dataTable td.txt-color-greenDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-greenDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-greenDark{color:#4f9e4f !important}.txt-color.txt-color-redLight,.txt-color-redLight.pf-help-light,.txt-color-redLight.pf-help,.dataTable td.txt-color-redLight.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-redLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-redLight{color:#a65858 !important}.txt-color.txt-color-red,.txt-color-red.pf-help-light,.pf-help-light.pf-log-error,.txt-color-red.pf-help,.pf-help.pf-log-error,.dataTable td.txt-color-red.pf-table-link-cell,.dataTable td.pf-table-link-cell.pf-log-error,.dataTable td.pf-table-action-cell>.txt-color-red.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-error,.txt-color.pf-log-error,.pf-landing .pf-landing-list li>i.pf-log-error,.pf-landing .pf-landing-list li>i.txt-color-red{color:#d9534f !important}.txt-color.txt-color-redDarker,.txt-color-redDarker.pf-help-light,.txt-color-redDarker.pf-help,.dataTable td.txt-color-redDarker.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-redDarker.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-redDarker{color:#a52521 !important}.txt-color.txt-color-yellow,.txt-color-yellow.pf-help-light,.txt-color-yellow.pf-help,.dataTable td.txt-color-yellow.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-yellow.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-yellow{color:#e2ce48 !important}.txt-color.txt-color-orangeLight,.txt-color-orangeLight.pf-help-light,.txt-color-orangeLight.pf-help,.dataTable td.txt-color-orangeLight.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-orangeLight.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orangeLight{color:#f0ad4e !important}.txt-color.txt-color-orange,.txt-color-orange.pf-help-light,.txt-color-orange.pf-help,.dataTable td.txt-color-orange.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-orange.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell:hover>.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orange{color:#e28a0d !important}.txt-color.txt-color-orangeDark,.txt-color-orangeDark.pf-help-light,.txt-color-orangeDark.pf-help,.dataTable td.txt-color-orangeDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-orangeDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-orangeDark{color:#c2760c !important}.txt-color.txt-color-pink,.txt-color-pink.pf-help-light,.txt-color-pink.pf-help,.dataTable td.txt-color-pink.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-pink.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-pink{color:#e06fdf !important}.txt-color.txt-color-pinkDark,.txt-color-pinkDark.pf-help-light,.txt-color-pinkDark.pf-help,.dataTable td.txt-color-pinkDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-pinkDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-pinkDark{color:#a8829f !important}.txt-color.txt-color-purple,.txt-color-purple.pf-help-light,.txt-color-purple.pf-help,.dataTable td.txt-color-purple.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-purple.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-purple{color:#6e587a !important}.txt-color.txt-color-darken,.txt-color-darken.pf-help-light,.txt-color-darken.pf-help,.dataTable td.txt-color-darken.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-darken.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-darken{color:#404040 !important}.txt-color.txt-color-lighten,.txt-color-lighten.pf-help-light,.txt-color-lighten.pf-help,.dataTable td.txt-color-lighten.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-lighten.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-lighten{color:#d5e7ec !important}.txt-color.txt-color-white,.txt-color-white.pf-help-light,.txt-color-white.pf-help,.dataTable td.txt-color-white.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-white.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-white{color:#fff !important}.txt-color.txt-color-magenta,.txt-color-magenta.pf-help-light,.txt-color-magenta.pf-help,.dataTable td.txt-color-magenta.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-magenta.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-magenta{color:#6e3671 !important}.txt-color.txt-color-tealLightest,.txt-color-tealLightest.pf-help-light,.txt-color-tealLightest.pf-help,.dataTable td.txt-color-tealLightest.pf-table-link-cell,.dataTable td.pf-table-link-cell:hover,.dataTable td.pf-table-action-cell>.txt-color-tealLightest.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-tealLightest{color:#6caead !important}.txt-color.txt-color-tealLighter,.txt-color-tealLighter.pf-help-light,.txt-color-tealLighter.pf-help,.dataTable td.txt-color-tealLighter.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-tealLighter.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i{color:#568a89 !important}.txt-color.txt-color-teal,.txt-color-teal.pf-help-light,.txt-color-teal.pf-help,.dataTable td.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-teal.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>td.pf-table-action-icon-cell.pf-table-link-cell,.pf-landing .pf-landing-list li>i.txt-color-teal{color:#477372 !important}.txt-color.txt-color-indigoDark,.txt-color-indigoDark.pf-help-light,.txt-color-indigoDark.pf-help,.dataTable td.txt-color-indigoDark.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-indigoDark.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-indigoDark{color:#5c6bc0 !important}.txt-color.txt-color-indigoDarkest,.txt-color-indigoDarkest.pf-help-light,.txt-color-indigoDarkest.pf-help,.dataTable td.txt-color-indigoDarkest.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-indigoDarkest.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-indigoDarkest{color:#313966 !important}.txt-color.txt-color-gold,.txt-color-gold.pf-help-light,.txt-color-gold.pf-help,.dataTable td.txt-color-gold.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-gold.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-gold{color:#cfb53b !important}.txt-color.txt-color-silver,.txt-color-silver.pf-help-light,.txt-color-silver.pf-help,.dataTable td.txt-color-silver.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-silver.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-silver{color:silver !important}.txt-color.txt-color-bronze,.txt-color-bronze.pf-help-light,.txt-color-bronze.pf-help,.dataTable td.txt-color-bronze.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-bronze.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-bronze{color:#8c7853 !important}.txt-color.txt-color-primary,.txt-color-primary.pf-help-light,.txt-color-primary.pf-help,.dataTable td.txt-color-primary.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-primary.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-primary{color:#375959 !important}.txt-color.txt-color-success,.txt-color-success.pf-help-light,.txt-color-success.pf-help,.dataTable td.txt-color-success.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-success.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-success{color:#4f9e4f !important}.txt-color.txt-color-information,.txt-color-information.pf-help-light,.txt-color-information.pf-help,.dataTable td.txt-color-information.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-information.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-information{color:#316490 !important}.txt-color.txt-color-warning,.txt-color-warning.pf-help-light,.pf-help-light.pf-log-warning,.txt-color-warning.pf-help,.pf-help.pf-log-warning,.dataTable td.txt-color-warning.pf-table-link-cell,.dataTable td.pf-table-link-cell.pf-log-warning,.dataTable td.pf-table-action-cell>.txt-color-warning.pf-table-action-icon-cell,.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell.pf-log-warning,.txt-color.pf-log-warning,.pf-landing .pf-landing-list li>i.pf-log-warning,.pf-landing .pf-landing-list li>i.txt-color-warning{color:#e28a0d !important}.txt-color.txt-color-danger,.txt-color-danger.pf-help-light,.txt-color-danger.pf-help,.dataTable td.txt-color-danger.pf-table-link-cell,.dataTable td.pf-table-action-cell>.txt-color-danger.pf-table-action-icon-cell,.pf-landing .pf-landing-list li>i.txt-color-danger{color:#a52521 !important}.bg-color.bg-color-blue{background-color:#428bca !important}.bg-color.bg-color-blueLight{background-color:#92a2a8 !important}.bg-color.bg-color-blueDark{background-color:#4c4f53 !important}.bg-color.bg-color-green{background-color:#5cb85c !important}.bg-color.bg-color-greenLight{background-color:#71843f !important}.bg-color.bg-color-greenDark{background-color:#496949 !important}.bg-color.bg-color-red{background-color:#d9534f !important}.bg-color.bg-color-yellow{background-color:#e2ce48 !important}.bg-color.bg-color-orange{background-color:#e28a0d !important}.bg-color.bg-color-orangeDark{background-color:#c2760c !important}.bg-color.bg-color-pink{background-color:#e06fdf !important}.bg-color.bg-color-pinkDark{background-color:#a8829f !important}.bg-color.bg-color-purple{background-color:#6e587a !important}.bg-color.bg-color-darken{background-color:#404040 !important}.bg-color.bg-color-lighten{background-color:#d5e7ec !important}.bg-color.bg-color-white{background-color:#fff !important}.bg-color.bg-color-gray{background-color:#3c3f41 !important}.bg-color.bg-color-grayDark{background-color:#525252 !important}.bg-color.bg-color-grayDarker{background-color:#2b2b2b !important}.bg-color.bg-color-magenta{background-color:#6e3671 !important}.bg-color.bg-color-tealLighter{background-color:#568a89 !important}.bg-color.bg-color-tealDarker{background-color:#212C30 !important}.bg-color.bg-color-tealDarkest{background-color:#1b2326 !important}.bg-color.bg-color-redLight{background-color:#a65858 !important}.pf-animation-slide-in{-moz-animation-duration:1.2s;-webkit-animation-duration:1.2s;-moz-animation-name:pfSlideIn;-webkit-animation-name:pfSlideIn;position:relative}@-webkit-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-moz-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-ms-keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@keyframes pfSlideIn{from{opacity:0;top:-20px}to{opacity:1;top:0px}}@-webkit-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@-moz-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@-ms-keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}@keyframes pfPulseDanger{0%{fill:#d9534f}50%{fill:#58100d}100%{fill:#d9534f}}.pf-animation-pulse-success{-webkit-animation:pulseBackgroundSuccess 1.5s 1;animation:pulseBackgroundSuccess 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-success .sorting_1{-webkit-animation:pulseBackgroundSuccessActive 1.5s 1;animation:pulseBackgroundSuccessActive 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-warning{-webkit-animation:pulseBackgroundWarning 1.5s 1;animation:pulseBackgroundWarning 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}.pf-animation-pulse-warning .sorting_1{-webkit-animation:pulseBackgroundWarningActive 1.5s 1;animation:pulseBackgroundWarningActive 1.5s 1;-webkit-animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38);animation-timing-function:cubic-bezier(0.53, -0.03, 0.68, 0.38)}@-webkit-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-moz-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-ms-keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@keyframes pulseBackgroundSuccess{5%{background-color:#4f9e4f;color:#313335}}@-webkit-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-moz-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-ms-keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@keyframes pulseBackgroundSuccessActive{5%{background-color:#478d47;color:#313335}}@-webkit-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-moz-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-ms-keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@keyframes pulseBackgroundWarning{5%{background-color:#e28a0d;color:#2b2b2b}}@-webkit-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@-moz-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@-ms-keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}@keyframes pulseBackgroundWarningActive{5%{background-color:#ca7b0c;color:#2b2b2b}}.pf-animate-rotate,.modal-content h2[data-toggle="collapse"]:after,.modal-content h4[data-toggle="collapse"]:after{-webkit-transition:all 0.08s linear;transition:all 0.08s linear}.pf-animate-rotate.right,.modal-content h2.right[data-toggle="collapse"]:after,.modal-content h4.right[data-toggle="collapse"]:after,.modal-content h2[data-toggle="collapse"].collapsed:after,.modal-content h4[data-toggle="collapse"].collapsed:after{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.pf-animate-rotate.left,.modal-content h2.left[data-toggle="collapse"]:after,.modal-content h4.left[data-toggle="collapse"]:after{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg)}body{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.pf-body{overflow:hidden}a{color:#477372;will-change:color;text-decoration:none;-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}a:hover{color:#6caead;text-decoration:none}a:focus{color:#477372}em{font-style:italic}em.pf-brand{text-transform:uppercase}.pf-font-capitalize{text-transform:capitalize}.no-padding{padding:0 !important}::-webkit-scrollbar{width:16px;height:16px}::-webkit-scrollbar-track{background-color:#2b2b2b;border-left:1px solid #313335;border-radius:2px;-webkit-transition:background-color 0.5s;transition:background-color 0.5s}::-webkit-scrollbar-thumb{height:6px;border:5px solid transparent;background-clip:padding-box;-webkit-border-radius:8px;background-color:#868c90}::-webkit-scrollbar-thumb:hover{background-color:#a1a5a8}::-webkit-scrollbar-button{width:0;height:0;display:none}::-webkit-scrollbar-corner{background-color:transparent}::selection{background:#adadad;color:#1d1d1d}::-moz-selection{background:#adadad;color:#1d1d1d}.pf-help-default,.pf-help-light,.pf-help{cursor:help;-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}.pf-dialog-icon-button,.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty,.pf-sig-table-module .pf-sig-table .fa-plus,.pf-system-route-module .pf-system-route-table td .fa-refresh,.pf-system-route-module .pf-system-route-table td .fa-search-plus{cursor:pointer;margin-top:2px;-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out}.pf-dialog-icon-button:not(.collapsed),.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty:not(.collapsed),.pf-sig-table-module .pf-sig-table .fa-plus:not(.collapsed),.pf-system-route-module .pf-system-route-table td .fa-refresh:not(.collapsed),.pf-system-route-module .pf-system-route-table td .fa-search-plus:not(.collapsed),.pf-dialog-icon-button:hover,.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty:hover,.pf-sig-table-module .pf-sig-table .fa-plus:hover,.pf-system-route-module .pf-system-route-table td .fa-refresh:hover,.pf-system-route-module .pf-system-route-table td .fa-search-plus:hover{color:#e28a0d}.pf-module-icon-button{cursor:pointer;-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out}.pf-module-icon-button:hover{color:#e28a0d !important}a.disabled{color:#777;pointer-events:none;cursor:default}.alert{will-change:opacity, transform}.editable-input optgroup[label]{background-color:#3c3f41;color:#63676a}.editable-input optgroup[label] option{background-color:#313335;color:#adadad;font-family:Consolas,monospace,Menlo,Monaco,"Courier New"}select:active,select:hover{outline:none}select:active,select:hover{outline-color:red}.select2-results [class*="col-"]{line-height:22px}.select2 ::-webkit-search-cancel-button{-webkit-appearance:none !important}.select2 .select2-selection__choice__remove{float:left}.select2 .select2-selection--multiple input{box-shadow:none !important}.dataTable.pf-table-fixed{width:100%;table-layout:fixed}.dataTable th.pf-table-image-cell,.dataTable th.pf-table-image-small-cell{padding-left:0 !important;padding-right:0 !important}.dataTable th.sorting,.dataTable th.sorting_asc,.dataTable th.sorting_desc{padding-right:18px !important}.dataTable td.pf-table-link-cell{cursor:pointer;-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}.dataTable td.pf-table-action-cell{cursor:pointer}.dataTable td.pf-table-action-cell>.pf-table-action-icon-cell{-webkit-transition:color 0.08s ease-out;transition:color 0.08s ease-out}.dataTable td.pf-table-image-cell{padding:0 !important}.dataTable td.pf-table-image-cell img{width:26px;box-sizing:content-box;border-left:1px solid #3c3f41;border-right:1px solid #3c3f41}.dataTable td.pf-table-image-small-cell img{width:24px;border-left:1px solid transparent;border-right:1px solid transparent}.dataTable td.pf-table-button-sm-cell{padding:0}.dataTable td.pf-table-counter-cell{color:#63676a}.dataTable td.pf-table-counter-cell .pf-digit-counter-small{width:20px;display:inline-block;font-size:10px}.dataTable td.pf-table-counter-cell .pf-digit-counter-large{width:26px;display:inline-block;font-size:10px}.dataTable td .pf-table-cell-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dataTable td .pf-table-cell-80{width:90px}.dataTable td .pf-table-cell-90{width:100px}.dataTable td .pf-table-cell-100{width:110px}.dataTable td.pf-table-cell-ellipses-auto{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dataTable td.separator-right,.dataTable th.separator-right{border-right:1px solid #3c3f41}.dataTable td svg.peity,.dataTable th svg.peity{display:block}table tr.collapsing{-webkit-transition:height 0.01s ease;transition:height 0.01s ease}table tr.collapse.in{display:table-row !important}.pf-table-tools{height:45px}.pf-table-tools .btn:not(:last-child){margin-right:10px}.pf-table-tools-action{will-change:height, opacity, display;opacity:0;display:none;height:0;visibility:hidden}.pf-loading-overlay{position:absolute;width:100%;height:100%;top:0;left:0;opacity:0;background:#2b2b2b;z-index:1060;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-loading-overlay .pf-loading-overlay-wrapper{width:25px;height:25px;margin:auto;text-align:center;position:absolute;top:0;left:0;bottom:0;right:0}.pf-loading-overlay .pf-loading-overlay-wrapper i{padding:3px}.navbar-header-block{width:100%}.navbar-header-block .navbar-nav{width:100%}.navbar-nav li:not(.disabled):hover:before,.navbar-nav li:not(.disabled).active:before{top:-4px;opacity:1}.navbar-nav li:not(.disabled):before{content:'';position:absolute;background-color:#5cb85c;opacity:0;will-change:opacity,top;-webkit-transition:top 0.15s ease-out,opacity 0.15s ease-out;transition:top 0.15s ease-out,opacity 0.15s ease-out;width:100%;height:2px;top:0}.pf-navbar-version-info{cursor:pointer}.pf-site{will-change:transform}.sb-slidebar{will-change:transform}.sb-left .list-group-item{cursor:pointer;-webkit-box-shadow:inset -10px 0px 5px -5px rgba(0,0,0,0.4);box-shadow:inset -10px 0px 5px -5px rgba(0,0,0,0.4)}.sb-right .list-group-item{cursor:pointer;-webkit-box-shadow:inset 10px 0px 5px -5px rgba(0,0,0,0.4);box-shadow:inset 10px 0px 5px -5px rgba(0,0,0,0.4)}.mCSB_container,.mCSB_dragger{will-change:top, left}.pf-timestamp-counter{visibility:hidden}.pf-map-type-private{color:#7986cb}.pf-map-type-corporation{color:#5cb85c}.pf-map-type-alliance{color:#428bca}.pf-map-type-global{color:#568a89}#pf-map-module{margin:20px 10px 0 10px}#pf-map-module #pf-map-tabs .pf-map-type-tab-default{border-top:2px solid transparent}#pf-map-module #pf-map-tabs .pf-map-type-tab-private{border-top:2px solid #7986cb}#pf-map-module #pf-map-tabs .pf-map-type-tab-corporation{border-top:2px solid #5cb85c}#pf-map-module #pf-map-tabs .pf-map-type-tab-alliance{border-top:2px solid #428bca}#pf-map-module #pf-map-tabs .pf-map-type-tab-global{border-top:2px solid #568a89}#pf-map-module #pf-map-tabs .pf-map-tab-icon{margin-right:5px}#pf-map-module #pf-map-tabs .pf-map-tab-shared-icon{margin-left:5px}.pf-map-content-row{margin-top:10px;padding-bottom:40px}.pf-map-content-row .pf-module{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;background:rgba(60,63,65,0.3);padding:10px;width:100%;margin-bottom:10px;will-change:height, transform, opacity;overflow:hidden;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-map-content-row .pf-module:before{content:'';position:absolute;top:0;left:0;border-style:solid;border-width:0 0 8px 8px;border-color:transparent transparent transparent #3c3f41;cursor:pointer}.pf-map-content-row .pf-module .label{margin-bottom:10px}.pf-map-content-row .pf-module .pf-dynamic-area{background:rgba(43,43,43,0.4)}.pf-map-content-row .pf-module h5 .pf-module-icon-button{margin-left:5px}.pf-user-status{color:#a52521}.pf-user-status-corp{color:#5cb85c}.pf-user-status-ally{color:#428bca}.pf-user-status-own{color:#7986cb}.pf-system-effect{display:none;cursor:default;color:#adadad}.pf-system-effect-magnetar{color:#e06fdf;display:inline-block}.pf-system-effect-redgiant{color:#d9534f;display:inline-block}.pf-system-effect-pulsar{color:#428bca;display:inline-block}.pf-system-effect-wolfrayet{color:#e28a0d;display:inline-block}.pf-system-effect-cataclysmic{color:#ffb;display:inline-block}.pf-system-effect-blackhole{color:#000;display:inline-block}.pf-system-info-rally .pf-system-head{background-color:#782d77;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjEuMCIgeDI9IjAuMCIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiMzZTI2NGUiLz48c3RvcCBvZmZzZXQ9IjI1JSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzNlMjY0ZSIvPjxzdG9wIG9mZnNldD0iNzUlIiBzdG9wLWNvbG9yPSIjM2UyNjRlIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-moz-linear-gradient(135deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-image:-webkit-linear-gradient(135deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-image:linear-gradient(-45deg, #3e264e 25%,rgba(0,0,0,0) 25%,rgba(0,0,0,0) 50%,#3e264e 50%,#3e264e 75%,rgba(0,0,0,0) 75%,rgba(0,0,0,0));background-size:25px 25px;-webkit-animation:move 3s linear infinite;-moz-animation:move 3s linear infinite;-ms-animation:move 3s linear infinite;animation:move 3s linear infinite}.pf-system-security-0-0{color:#be0000}.pf-system-security-0-1{color:#ab2600}.pf-system-security-0-2{color:#be3900}.pf-system-security-0-3{color:#c24e02}.pf-system-security-0-4{color:#ab5f00}.pf-system-security-0-5{color:#bebe00}.pf-system-security-0-6{color:#73bf26}.pf-system-security-0-7{color:#00bf00}.pf-system-security-0-8{color:#00bf39}.pf-system-security-0-9{color:#39bf99}.pf-system-security-1-0{color:#28c0bf}.pf-system-sec{margin-right:5px;cursor:-moz-grab;cursor:-webkit-grab}.pf-system-sec-highSec{color:#5cb85c}.pf-system-sec-lowSec{color:#e28a0d}.pf-system-sec-nullSec{color:#d9534f}.pf-system-sec-high{color:#d9534f}.pf-system-sec-mid{color:#e28a0d}.pf-system-sec-low{color:#428bca}.pf-system-sec-unknown{color:#7986cb}.pf-system-status-friendly{border-color:#428bca !important;color:#428bca}.pf-system-status-occupied{border-color:#e28a0d !important;color:#e28a0d}.pf-system-status-hostile{border-color:#d9534f !important;color:#d9534f}.pf-system-status-empty{border-color:#5cb85c !important;color:#5cb85c}.pf-system-status-unscanned{border-color:#568a89 !important;color:#568a89}.pf-system-info-status-label{background-color:#63676a;color:#000;will-change:background-color;-webkit-transition:background-color 0.5s ease-out;transition:background-color 0.5s ease-out}.pf-system-info-status-label.pf-system-status-friendly{background-color:#428bca}.pf-system-info-status-label.pf-system-status-occupied{background-color:#e28a0d}.pf-system-info-status-label.pf-system-status-hostile{background-color:#d9534f}.pf-system-info-status-label.pf-system-status-empty{background-color:#5cb85c}.pf-system-info-status-label.pf-system-status-unscanned{background-color:#568a89}.pf-system-effect-dialog-wrapper .table,.pf-jump-info-dialog .table{margin:15px 0}.pf-system-effect-dialog-wrapper .table td,.pf-jump-info-dialog .table td{text-transform:capitalize}.pf-fake-connection{box-sizing:content-box;display:inline-block;width:70px;height:4px;margin-right:5px;border-top:2px solid #63676a;border-bottom:2px solid #63676a;background-color:#3c3f41;position:relative;font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif}.pf-fake-connection.pf-map-connection-stargate{background-color:#313966;border-color:#63676a}.pf-fake-connection.pf-map-connection-jumpbridge{background-color:#6caead;border-color:#3c3f41;background:repeating-linear-gradient(to right, #6caead, #6caead 10px, #3c3f41 10px, #3c3f41 20px)}.pf-fake-connection.pf-map-connection-wh-eol{border-color:#d747d6}.pf-fake-connection.pf-map-connection-wh-reduced{background-color:#e28a0d}.pf-fake-connection.pf-map-connection-wh-critical{background-color:#a52521}.pf-fake-connection.pf-map-connection-frig{border-style:dashed;border-left:none;border-right:none}.pf-fake-connection.pf-map-connection-frig:after{content:'frig';background-color:#e28a0d;color:#1d1d1d;padding:0px 3px;position:absolute;left:25px;top:-6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.pf-fake-connection.pf-map-connection-preserve-mass:after{content:'save mass';background-color:#a52521;color:#eaeaea;padding:0px 3px;position:absolute;left:9px;top:-6px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.tooltip-inner{color:#5cb85c;background-color:#3c3f41;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;padding:5px 5px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.modal .tooltip{z-index:1060}.modal .tooltip .tooltip-inner{color:#313335;background-color:#adadad}.tooltip.top .tooltip-arrow{border-top-color:#63676a}.tooltip.right .tooltip-arrow{border-right-color:#63676a}.tooltip.bottom .tooltip-arrow{border-bottom-color:#63676a}.tooltip.left .tooltip-arrow{border-left-color:#63676a}.popover{z-index:1060;max-width:600px}.popover img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.popover h4{color:#adadad}.popover table{color:#adadad;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;line-height:16px;font-size:11px}.popover table td{padding:0 5px;vertical-align:middle !important}.pf-popover{display:initial}.pf-popover .popover-content{padding:0}.pf-popover h6{white-space:nowrap;margin-right:50px}.pf-popover h6:before,.pf-popover h6:after{content:" ";display:table}.pf-popover h6:after{clear:both}.pf-popover .well{margin-top:7px;margin-bottom:10px}.pf-popover .list-group{margin:0}.pf-popover .list-group .list-group-item{color:#313335}.pf-popover .list-group .list-group-item:hover{color:#1d1d1d}.pf-popover .list-group .list-group-item.disabled{background-color:#3c3f41;color:#63676a;cursor:not-allowed}.pf-popover .list-group .list-group-item img{width:30px;margin:-8px 10px -6px -8px;border-radius:0}.pf-popover .list-group .list-group-item i{margin-right:20px}td.pf-popover-trigger:hover{color:#477372}.pf-notransition{-webkit-transition:none !important;-moz-transition:none !important;-o-transition:none !important;transition:none !important}.pf-dynamic-area{padding:10px;min-height:100px;position:relative;background-color:#313335;overflow:hidden;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-dynamic-area .dl-horizontal{margin-bottom:0}.pf-dynamic-area .dl-horizontal dd{min-width:100px}.pf-dynamic-area .dl-horizontal dd.txt-color,.pf-dynamic-area .dl-horizontal dd.pf-help-light,.pf-dynamic-area .dl-horizontal dd.pf-help,.pf-dynamic-area .dl-horizontal .dataTable td.pf-table-action-cell>dd.pf-table-action-icon-cell,.dataTable .pf-dynamic-area .dl-horizontal td.pf-table-action-cell>dd.pf-table-action-icon-cell{font-weight:bold}.pf-dynamic-area>[class~='alert']:last-of-type{margin-bottom:0}.pf-code-ObjectBrace{color:#782d77;font-weight:bold}.pf-code-ArrayBrace{color:#3e264e;font-weight:bold}.pf-code-PropertyName{color:#1d1d1d;font-weight:bold}.pf-code-String{color:#e28a0d}.pf-code-Number{color:#4f9e4f}.pf-code-Boolean{color:#313966;font-weight:bold}.pf-code-Function{color:#782d77}.pf-code-Null{color:#2b2b2b;font-weight:bold}.pf-code-Comma{color:#1d1d1d;font-weight:bold}code .fa,code .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list code li>i{color:#3c3f41;cursor:pointer}#pf-logo-wrapper{display:block}#pf-head{margin-bottom:0px}#pf-head a{-webkit-transition:color 0.15s ease-out;transition:color 0.15s ease-out;will-change:color}#pf-head a:focus{color:#477372}#pf-head a:focus img{border-color:#3c3f41}#pf-head a:hover{text-decoration:none}#pf-head a:hover .badge{color:#6caead}#pf-head a:hover img{border-color:#568a89}#pf-head i{margin-right:2px}#pf-head .pf-brand-desc{margin:6px 10px 0 90px;width:180px}#pf-head .pf-head-menu{padding:3px 10px;line-height:24px}#pf-head .pf-head-menu .pf-head-menu-logo{width:24px;height:24px;display:inline-block;float:left}#pf-head .pf-head-user-character,#pf-head .pf-head-user-ship{opacity:0;visibility:hidden}#pf-head .pf-head-active-user,#pf-head #pf-head-current-location{display:none}#pf-head .pf-head-active-user .badge,#pf-head #pf-head-current-location .badge{-webkit-transition:color 0.3s ease-out;transition:color 0.3s ease-out}#pf-head .pf-head-user-character-image,#pf-head .pf-head-user-ship-image{display:inline-block;margin-top:-6px;margin-bottom:-6px;width:27px;border:1px solid #3c3f41;margin-right:3px;-webkit-transition:border-color 0.15s ease-out;transition:border-color 0.15s ease-out;will-change:border-color}#pf-head .pf-head-program-status{cursor:pointer}#pf-head .navbar-text{min-width:60px}#pf-head .tooltip .tooltip-inner{color:#adadad}.pf-head{-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.pf-head .badge{background-color:#3c3f41;color:#adadad}.pf-head small{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}#pf-head-character-switch td{border:none}#pf-head-character-switch td:first-child+td{padding:0 5px}#pf-footer{position:absolute;bottom:0;left:0;width:100%;margin:0;background:rgba(60,63,65,0.3)}#pf-footer a{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;color:#375959}#pf-footer a:hover{color:#477372;text-decoration:none}.navbar-fixed-bottom{padding:2px 0}.navbar-fixed-bottom .container-fluid{padding-left:0;padding-right:0}#pf-global-info{position:absolute;left:0;bottom:32px;width:100%;height:32px;margin-bottom:0}@-webkit-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@-moz-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@-ms-keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}@keyframes move{0%{background-position:0 0}100%{background-position:50px 50px}}.pf-animate{visibility:hidden;opacity:0}.pf-color-line{position:fixed;top:0;left:0;width:100%;height:3px;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzY2Yzg0ZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzY2Yzg0ZiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #66c84f),color-stop(100%, #66c84f));background-image:-moz-linear-gradient(left, #66c84f,#66c84f 100%);background-image:-webkit-linear-gradient(left, #66c84f,#66c84f 100%);background-image:linear-gradient(to right, #66c84f,#66c84f 100%)}.pf-color-line.warning{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2UyOGEwZCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2UyOGEwZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #e28a0d),color-stop(100%, #e28a0d));background-image:-moz-linear-gradient(left, #e28a0d,#e28a0d 100%);background-image:-webkit-linear-gradient(left, #e28a0d,#e28a0d 100%);background-image:linear-gradient(to right, #e28a0d,#e28a0d 100%)}.pf-color-line.danger{background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E1MjUyMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2E1MjUyMSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #a52521),color-stop(100%, #a52521));background-image:-moz-linear-gradient(left, #a52521,#a52521 100%);background-image:-webkit-linear-gradient(left, #a52521,#a52521 100%);background-image:linear-gradient(to right, #a52521,#a52521 100%)}.pf-splash,.pf-splash-warning,.pf-splash-error{position:absolute;z-index:2000;background-color:#1d1d1d;color:#63676a;top:0;bottom:0;left:0;right:0;will-change:opacity}.pf-splash .pf-splash-title,.pf-splash-warning .pf-splash-title,.pf-splash-error .pf-splash-title{position:fixed;left:50%;top:30%;text-align:center;max-width:500px;padding:20px;-moz-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.pf-splash .pf-splash-debug,.pf-splash-warning .pf-splash-debug,.pf-splash-error .pf-splash-debug{position:absolute;bottom:0;width:100%}.pf-splash .pf-splash-debug .pf-splash-debug-headline,.pf-splash-warning .pf-splash-debug .pf-splash-debug-headline,.pf-splash-error .pf-splash-debug .pf-splash-debug-headline{padding:0 10px}.pf-splash .pf-splash-debug .pf-splash-pre,.pf-splash-warning .pf-splash-debug .pf-splash-pre,.pf-splash-error .pf-splash-debug .pf-splash-pre{margin-bottom:0}@media (max-width: 1200px){.pf-landing #pf-logo-container{margin:5px auto}.pf-landing .pf-brand-desc{display:none}.pf-landing .navbar .navbar-brand{margin-left:10px}}.pf-landing section{min-height:200px;padding:20px 0 40px 0;border-bottom:1px solid #2b2b2b}.pf-landing section h4{font-size:18px;font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;margin:5px 0 10px 0;border-bottom:1px solid #2b2b2b;line-height:34px}.pf-landing .container>.row{margin-bottom:30px}.pf-landing .alert{box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing a[data-gallery]{position:relative}.pf-landing a[data-gallery]:before{content:'\f002';font-family:'FontAwesome';font-size:20px;line-height:20px;color:#e28a0d;position:absolute;top:9px;height:100%;width:100%;padding-top:calc(50% - 10px);z-index:10;text-align:center;-webkit-transition:transform 0.1s ease-out,opacity 0.1s ease-out;transition:transform 0.1s ease-out,opacity 0.1s ease-out;will-change:transform, opacity;transform:scale(1.3, 1.3);opacity:0}.pf-landing a[data-gallery]:hover img{border-color:#6caead;-webkit-filter:brightness(50%);filter:brightness(50%)}.pf-landing a[data-gallery]:hover:before{-webkit-transition-delay:.1s;transition-delay:.1s;transform:scale(1, 1);opacity:1}.pf-landing a[data-gallery] .pf-landing-image-preview{border-width:1px;border-style:solid;border-color:#1d1d1d;margin:5px 0 15px 0;display:inline-block;will-change:all;-webkit-filter:brightness(100%);filter:brightness(100%);-webkit-transition:all 0.2s ease-out;transition:all 0.2s ease-out;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing a[data-gallery] .pf-landing-image-preview.pf-landing-image-preview-small{height:160px}.pf-landing a[data-gallery] .pf-landing-image-preview.pf-landing-image-preview-medium{height:256px}#pf-landing-top{height:355px;border-bottom:1px solid #313335;position:relative}#pf-landing-top:before{content:'';width:100%;height:100%;position:absolute;background:url("../img/pf-bg.jpg") #05050a;background-repeat:no-repeat;background-position:0 0;-webkit-filter:brightness(.9);filter:brightness(.9)}#pf-landing-top #pf-logo-container{-moz-transform:scale3d(0.8, 0.8, 1);-ms-transform:scale3d(0.8, 0.8, 1);-webkit-transform:scale3d(0.8, 0.8, 1);transform:scale3d(0.8, 0.8, 1)}#pf-landing-top #pf-header-container{position:absolute;width:100%;background-position:center center}#pf-landing-top #pf-header-container #pf-header-canvas{position:absolute;visibility:hidden;top:0;left:0}#pf-landing-top #pf-header-container #pf-logo-container{z-index:110}#pf-landing-top #pf-header-container #pf-header-preview-container{position:absolute;left:400px;width:590px;height:350px;top:37px}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element{position:relative;margin-left:12px;margin-top:12px;height:155px;width:180px;padding:7px;opacity:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;background-color:rgba(43,43,43,0.5)}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element:nth-child(n+4){box-shadow:0 4px 10px rgba(0,0,0,0.4)}#pf-landing-top #pf-header-container #pf-header-preview-container .pf-header-preview-element:after{content:'';position:absolute;width:calc(100% - 14px);height:calc(100% - 14px);-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-repeat:no-repeat;background-position:50% 50%;background-color:rgba(29,29,29,0.75)}#pf-landing-top .container{position:relative;margin-top:10px}#pf-header-preview-intel:after{background-image:url("../img/landing/intel.png")}#pf-header-preview-map:after{background-image:url("../img/landing/map.png")}#pf-header-preview-scope:after{background-image:url("../img/landing/scope.png")}#pf-header-preview-signature:after{background-image:url("../img/landing/signature.png")}#pf-header-preview-data:after{background-image:url("../img/landing/data.png")}#pf-header-preview-gameplay:after{background-image:url("../img/landing/gameplay.png")}#pf-landing-login{padding-top:40px;padding-bottom:30px}#pf-landing-login .row{margin-bottom:0}#pf-landing-login .pf-character-selection>div:not(.pf-character-row-animate){-webkit-transition:width 0.2s ease,margin 0.2s ease;transition:width 0.2s ease,margin 0.2s ease}#pf-landing-login .pf-dynamic-area{display:inline-block;margin:10px 5px 20px 5px;padding:10px 10px 5px 10px;min-width:155px;min-height:184px;overflow:visible;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}#pf-landing-login .pf-dynamic-area .ribbon-wrapper{z-index:5}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper{opacity:0;width:128px;border:2px solid #63676a;-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px;-webkit-transition:border-color 0.2s ease-out,box-shadow 0.2s ease-out;transition:border-color 0.2s ease-out,box-shadow 0.2s ease-out;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);will-change:border-color, transition;overflow:hidden;cursor:pointer;display:inline-block;background-color:#2b2b2b;box-sizing:content-box}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover{border-color:#4f9e4f}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover .pf-character-name{color:#4f9e4f}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper:hover .pf-character-image{-webkit-filter:grayscale(50%);filter:grayscale(50%)}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper.pf-character-active:after{font-family:FontAwesome;content:"\f111";position:absolute;top:5px;left:5px;height:14px;width:14px;color:#5cb85c;font-size:10px}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image{overflow:hidden;width:128px;height:128px;position:relative}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image .pf-character-info{position:absolute;top:0;left:0;width:0;height:100%;color:#adadad;background:rgba(60,63,65,0.8);overflow:hidden;will-change:width, transition;padding:10px 0}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-select-image .pf-character-info .pf-character-info-text{line-height:25px}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-name{font-size:13px;line-height:30px;border-top:1px solid #313335;color:#adadad;-webkit-transition:color 0.2s ease-out;transition:color 0.2s ease-out}#pf-landing-login .pf-dynamic-area .pf-character-image-wrapper .pf-character-image{-webkit-transition:all 0.3s ease-out;transition:all 0.3s ease-out;-webkit-filter:grayscale(0%);filter:grayscale(0%)}#pf-landing-login .pf-sso-login-button{position:relative;display:inline-block;width:270px;height:45px;border:none;margin-bottom:10px;background-color:transparent;background-image:url("../img/landing/eve_sso_login_buttons_large_black_hover.png");cursor:pointer;box-shadow:0 2px 5px rgba(0,0,0,0.2);-webkit-transition:box-shadow 0.12s ease-out;transition:box-shadow 0.12s ease-out;will-change:box-shadow}#pf-landing-login .pf-sso-login-button:after{content:' ';position:absolute;width:270px;height:45px;left:0;top:0;background-image:url("../img/landing/eve_sso_login_buttons_large_black.png");-webkit-transition:opacity 0.12s ease-in-out;transition:opacity 0.12s ease-in-out;will-change:opacity}#pf-landing-login .pf-sso-login-button:hover{box-shadow:0 4px 5px rgba(0,0,0,0.2)}#pf-landing-login .pf-sso-login-button:hover:after{opacity:0}#pf-landing-login .pf-sso-login-button.disabled{pointer-events:auto}#pf-landing-login #pf-notification-panel{display:none}#pf-header-map{position:relative;margin:0 auto;height:380px;width:600px;pointer-events:none}#pf-header-map .pf-header-svg-layer{position:absolute;top:0;left:0;right:0;bottom:0}#pf-header-map #pf-header-systems{z-index:100}#pf-header-map #pf-header-connectors{z-index:90}#pf-header-map #pf-header-connections{z-index:80}#pf-header-map #pf-header-background{z-index:70}#pf-header-map #pf-header-background .pf-header-system{display:none}#pf-header-map-bg{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}#pf-header-map-bg img{pointer-events:none}#pf-header-map-bg #pf-map-bg-image{opacity:0;position:absolute;bottom:0;right:0;width:100%;height:100%}#pf-header-map-bg #pf-map-neocom{opacity:0;height:665px;width:21px}#pf-header-map-bg #pf-map-browser{opacity:0;position:absolute;top:110px;left:21px;height:560px;width:515px}#pf-landing-gallery-carousel{background-image:url("../img/pf-header-bg.jpg")}#pf-landing-gallery-carousel .slide-content{border-radius:5px;pointer-events:none}#pf-landing-gallery-carousel h3{width:100%;text-align:left}.pf-landing-pricing-panel{margin-top:20px}.pricing-big{-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pricing-big .panel-heading{border-color:#3c3f41}.pricing-big .the-price{padding:1px 0;background:#2d3031;text-align:center}.pricing-big .the-price .subscript{font-size:12px;color:#63676a}.pricing-big .price-features{background:#3c3f41;color:#adadad;padding:20px 15px;line-height:22px}.pricing-big .price-features:not(.price-features-fluid){min-height:205px}.pricing-big .price-features .list-unstyled.text-left li,.pricing-big .price-features .text-left.list-inline li{text-indent:-1em;padding-left:1.5em}.pricing-big .price-features .list-unstyled.text-left li .fa,.pricing-big .price-features .text-left.list-inline li .fa,.pricing-big .price-features .list-unstyled.text-left .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pricing-big .price-features .list-unstyled.text-left li>i,.pricing-big .price-features .text-left.list-inline .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pricing-big .price-features .text-left.list-inline li>i{text-indent:0}.pricing-big table tr td{line-height:1}#pf-landing-admin .pf-landing-admin-login{margin-bottom:0}#pf-landing-about .pf-landing-about-me{width:256px;height:256px;border:none;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4)}.pf-landing-footer{padding:30px 0;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:#171717}.pf-landing-footer .row{margin-bottom:0 !important}.pf-landing-footer .pf-social-networks>li{display:inline-block;line-height:1}.pf-landing-footer .pf-social-networks>li a{display:inline-block;background:rgba(99,103,106,0.5);line-height:24px;text-align:center;font-size:12px;margin-right:5px;width:28px;height:24px}#pf-static-logo-svg{opacity:0;position:absolute;z-index:105;overflow:visible}#pf-static-logo-svg path{will-change:fill, opacity, transform, translateZ, translateX, translateY;pointer-events:all;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.logo-ploygon-top-right{fill:#477372;fill-rule:evenodd;stroke:#477372;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-bottom-left{fill:#5cb85c;fill-rule:evenodd;stroke:#5cb85c;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-bottom-right{fill:#375959;fill-rule:evenodd;stroke:#375959;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1}.logo-ploygon-top-left{fill:#63676a;fill-opacity:1;fill-rule:evenodd;stroke:#63676a;stroke-width:0px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1}@-webkit-keyframes bounce{0%, 20%, 50%, 80%, 100%{-webkit-transform:translateY(0)}40%{-webkit-transform:translateY(-8px)}60%{-webkit-transform:translateY(-4px)}}@keyframes bounce{0%, 20%, 50%, 80%, 100%{transform:translateY(0)}40%{transform:translateY(-8px)}60%{transform:translateY(-4px)}}#pf-map-tab-element{max-width:2515px;margin:0 auto}.pf-map-tab-content .pf-map-wrapper{position:relative;width:100%;max-width:2515px;height:550px;overflow:auto;padding:5px;background:rgba(43,43,43,0.93);box-shadow:inset -3px 3px 10px 0 rgba(0,0,0,0.3);border-bottom-right-radius:5px;border-bottom-left-radius:5px;border-width:1px;border-style:solid;border-color:#313335}.pf-map-tab-content .pf-map-wrapper:focus{border:1px solid #3c3f41}.pf-map-overlay{position:absolute;display:none;z-index:10000;right:10px;background:rgba(0,0,0,0.25);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.pf-map-overlay.pf-map-overlay-timer{bottom:23px;width:36px;height:36px}.pf-map-overlay.pf-map-overlay-info{top:8px;height:36px;min-height:36px;min-width:36px;color:#2b2b2b;padding:3px;line-height:26px}.pf-map-overlay.pf-map-overlay-info i{margin:0;margin-top:3px;width:0;height:26px;opacity:0;color:#63676a;transform:scale(0);transform-origin:50% 50% 0px;-webkit-transition:color 0.18s ease-in-out;transition:color 0.18s ease-in-out;cursor:help;will-change:all}.pf-map-overlay.pf-map-overlay-info i.fa,.pf-map-overlay.pf-map-overlay-info .pf-landing .pf-landing-list li>i,.pf-landing .pf-landing-list .pf-map-overlay.pf-map-overlay-info li>i{font-size:26px}.pf-map-overlay.pf-map-overlay-info i.glyphicon{margin-top:1px;font-size:24px;padding-left:3px}.pf-map-overlay.pf-map-overlay-info i.active,.pf-map-overlay.pf-map-overlay-info i:hover{color:#c2760c}.pf-map-overlay.pf-map-overlay-local{top:54px;min-height:80px;width:32px;display:block;will-change:width}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content{margin-right:36px;padding:5px 0 5px 5px;overflow:hidden}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline{font-size:12px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline .badge{margin-left:5px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-headline .pf-system-sec{cursor:default}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-local-table{font-size:10px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-local-table td{white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .dataTables_paginate,.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .dataTables_empty{white-space:nowrap}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-toolbar .pf-map-overlay-toolbar-icon{vertical-align:0;margin-top:14px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-content .pf-map-overlay-toolbar .pf-map-overlay-toolbar-checkbox{display:inline-block;margin-bottom:0}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main{position:absolute;top:0;right:0;height:100%;padding:3px;width:32px;cursor:pointer;text-align:center;border-left:1px solid #2b2b2b}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main .pf-map-overlay-local-trigger{margin-bottom:10px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-main i{font-size:12px}.pf-map-overlay.pf-map-overlay-local .pf-map-overlay-local-jumps{position:absolute;bottom:5px;width:calc(100% - 6px)}.pf-map-overlay.pf-map-overlay-local .badge{font-family:Arial, sans-serif;background-color:#2b2b2b}.pf-grid-small{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAG1JREFUeNrs18EJgDAQRNGJpoQQSC+CWMSWEwhYrCAWYRNz2MP/BQzvOiUi5Op5vzl6u+VrbUoeQIAAAQIECBAgQICpK8d5zay40dtenR+CTwIQIECAAAECBAgQYLaqpGX8EHLuSdIPAAD//wMAuMQN2uF+ypQAAAAASUVORK5CYII=') !important}.pf-map{width:2500px;height:520px;position:relative;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.pf-map .jsplumb-overlay{opacity:1;pointer-events:none;will-change:opacity;-webkit-transition:opacity 0.18s ease-out;transition:opacity 0.18s ease-out}.pf-map .jsplumb-hover.jsplumb-overlay{opacity:0 !important}.pf-map .jsplumb-hover:not(.jsplumb-overlay){-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-webkit-animation-name:bounce;animation-name:bounce}.pf-map .jsplumb-target-hover,.pf-map .jsplumb-source-hover{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-webkit-animation-name:bounce;animation-name:bounce;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.3);box-shadow:0 6px 12px rgba(0,0,0,0.3)}.pf-map .pf-system{position:absolute;min-width:60px;height:auto;overflow:hidden;background-color:#313335;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;z-index:100;will-change:top, left, opacity;border-width:2px;border-style:solid;border-color:#63676a;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-webkit-transition:border-color 0.5s ease-out,box-shadow 0.2s ease-out;transition:border-color 0.5s ease-out,box-shadow 0.2s ease-out;-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.pf-map .pf-system:hover{-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.3);box-shadow:0 6px 12px rgba(0,0,0,0.3);-moz-transform:translate3d(0, -2px, 0);-ms-transform:translate3d(0, -2px, 0);-webkit-transform:translate3d(0, -2px, 0);transform:translate3d(0, -2px, 0)}.pf-map .pf-system .pf-system-head{padding:0px 3px 0px 3px;cursor:pointer;font-family:Arial, sans-serif;font-weight:bold}.pf-map .pf-system .pf-system-head .pf-system-head-name{border:none;display:inline-block;min-width:41px;color:#adadad;margin-right:2px}.pf-map .pf-system .pf-system-head .fa-lock{display:none}.pf-map .pf-system .pf-system-head .pf-system-head-expand{margin-left:2px;color:#63676a;display:none}.pf-map .pf-system .pf-system-head .editable-empty{font-style:normal}.pf-map .pf-system .pf-system-body{height:0px;width:100%;overflow:hidden;cursor:-moz-grab;cursor:-webkit-grab;cursor:grab;padding:0 4px;white-space:nowrap;display:none;will-change:width;border-top-width:1px;border-top-style:dashed;border-top-color:#63676a}.pf-map .pf-system .pf-system-body .pf-system-body-item{color:#7c8184;font-size:10px;line-height:16px;height:16px}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-system-body-right{text-overflow:ellipsis;float:right;color:#f0ad4e;display:none}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-user-status{font-size:7px;width:10px;vertical-align:middle;height:14px}.pf-map .pf-system .pf-system-body .pf-system-body-item .pf-system-body-item-name{display:inline-block;width:65px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.pf-map .pf-system .tooltip.in{opacity:1}.pf-map .pf-system .tooltip .tooltip-inner{color:#313335;background-color:#adadad;padding:3px 3px}.pf-map .pf-system-active:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target){-webkit-box-shadow:#ffb 0px 0px 8px 0px;box-shadow:#ffb 0px 0px 8px 0px}.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target),.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target){-webkit-box-shadow:#58100d 0px 0px 8px 0px;box-shadow:#58100d 0px 0px 8px 0px}.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-head,.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-head,.pf-map .pf-system-selected:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-body,.pf-map .jsPlumb_dragged:not(.pf-map-endpoint-source):not(.pf-map-endpoint-target) .pf-system-body{background-color:#58100d}.pf-map .pf-system-locked .pf-system-sec{cursor:default !important}.pf-map .pf-system-locked .pf-system-body{cursor:default !important}.pf-map .pf-system-locked .fa-lock{color:#63676a !important;display:inline-block !important}.pf-map .pf-map-endpoint-source,.pf-map .pf-map-endpoint-target{z-index:90}.pf-map .pf-map-endpoint-source svg,.pf-map .pf-map-endpoint-target svg{overflow:visible}.pf-map .pf-map-endpoint-source svg circle,.pf-map .pf-map-endpoint-target svg circle{-webkit-transition:stroke 0.18s ease-out,fill 0.18s ease-out;transition:stroke 0.18s ease-out,fill 0.18s ease-out}.pf-map .pf-map-endpoint-source svg *,.pf-map .pf-map-endpoint-target svg *{stroke:#63676a;stroke-width:2;fill:#3c3f41;cursor:pointer}.pf-map .pf-map-endpoint-source:hover circle,.pf-map .pf-map-endpoint-target:hover circle{stroke:#e28a0d !important}.pf-map .pf-map-endpoint-source.jsplumb-hover,.pf-map .pf-map-endpoint-target.jsplumb-hover{z-index:95}.pf-map .pf-map-endpoint-source.jsplumb-dragging circle,.pf-map .pf-map-endpoint-target.jsplumb-dragging circle{stroke:#e28a0d}.pf-map .jsplumb-endpoint-drop-allowed circle{stroke:#5cb85c !important;fill:#5cb85c !important}.pf-map .jsplumb-endpoint-drop-forbidden circle{stroke:#a52521 !important;fill:#a52521 !important}.pf-map svg.jsplumb-connector{cursor:pointer;stroke-linecap:round;-webkit-transition:stroke 0.18s ease-out;transition:stroke 0.18s ease-out;will-change:all}.pf-map svg.jsplumb-connector path{-webkit-transition:stroke 0.18s ease-out;transition:stroke 0.18s ease-out}.pf-map svg.jsplumb-connector path:nth-child(2){stroke:#3c3f41}.pf-map svg.jsplumb-connector path:first-child{stroke:#63676a}.pf-map svg.jsplumb-connector.jsplumb-hover{z-index:80}.pf-map svg.jsplumb-connector.jsplumb-hover path:first-child{stroke:#eaeaea}.pf-map svg.jsplumb-connector.jsplumb-dragging{-webkit-transition:opacity 0.18s ease-out;transition:opacity 0.18s ease-out;opacity:0.4;z-index:80}.pf-map svg.pf-map-connection-jumpbridge{z-index:50}.pf-map svg.pf-map-connection-jumpbridge path:first-child{stroke:rgba(255,255,255,0)}.pf-map svg.pf-map-connection-jumpbridge path:nth-child(2){stroke:#568a89}.pf-map svg.pf-map-connection-jumpbridge:hover path:first-child{stroke:rgba(255,255,255,0)}.pf-map svg.pf-map-connection-jumpbridge:hover path:nth-child(2){stroke:#eaeaea}.pf-map svg.pf-map-connection-stargate{z-index:60}.pf-map svg.pf-map-connection-stargate path:first-child{stroke:#63676a}.pf-map svg.pf-map-connection-stargate path:nth-child(2){stroke:#313966}.pf-map svg.pf-map-connection-stargate:hover path:first-child{stroke:#eaeaea}.pf-map svg.pf-map-connection-wh-fresh,.pf-map svg.pf-map-connection-wh-reduced,.pf-map svg.pf-map-connection-wh-critical,.pf-map svg.pf-map-connection-wh-eol{z-index:70}.pf-map svg.pf-map-connection-wh-eol path:first-child{stroke:#d747d6}.pf-map svg.pf-map-connection-wh-eol:hover path:first-child{stroke:#eaeaea}.pf-map svg.pf-map-connection-wh-reduced path:nth-child(2){stroke:#e28a0d}.pf-map svg.pf-map-connection-wh-critical path:nth-child(2){stroke:#a52521}.pf-map .pf-map-connection-overlay{padding:1px 4px;font-size:11px;z-index:1020;background-color:#3c3f41;color:#adadad;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.pf-map .frig{background-color:#f0ad4e;color:#1d1d1d}.pf-map .mass{background-color:#a52521;color:#eaeaea}.pf-map .eol{background-color:#3c3f41;color:#d747d6}.pf-map .pf-map-connection-arrow-overlay{stroke:#313335;fill:#5cb85c}.pf-map .pf-map-connection-diamond-overlay{stroke:#313335;fill:#d9534f;animation-name:pfPulseDanger;animation-duration:4s;animation-iteration-count:infinite}.pf-map .pf-map-connection-small-overlay{font-family:Arial, sans-serif;padding:0 2px;font-size:10px;z-index:1020;background-color:#3c3f41;color:#adadad;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.4);box-shadow:0 6px 12px rgba(0,0,0,0.4)}.ui-dialog-content label{min-width:60px}.dropdown-menu{font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;z-index:1020;will-change:opacity, top, left, transform}.dropdown-menu a{cursor:pointer}.dropdown-menu i{width:20px;pointer-events:none}.dropdown-menu .dropdown-menu{border-top-left-radius:0;border-bottom-left-radius:0;clip-path:inset(-12px -12px -12px 0px)}.dropdown-menu[role]>li{position:relative}.dropdown-menu[role]>li:before{content:'';position:absolute;background-color:#5cb85c;opacity:0;will-change:opacity,left;-webkit-transition:left 0.15s ease-out,opacity 0.15s ease-out;transition:left 0.15s ease-out,opacity 0.15s ease-out;width:2px;height:100%;left:0}.dropdown-menu[role]>li:hover:before{left:-4px;opacity:1}.pf-system-tooltip-inner{color:#adadad;padding:2px 4px;min-width:25px;-webkit-transition:color 0.2s ease-out;transition:color 0.2s ease-out}.pf-system-info-module h5{text-transform:capitalize;line-height:16px}.pf-system-info-module .pf-system-info-description-area{min-height:123px}.pf-system-info-module .pf-system-info-description-area .editable-container{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group{width:100%}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group .editable-input{width:calc(100% - 75px)}.pf-system-info-module .pf-system-info-description-area .editable-container .editableform .form-group .editable-input textarea{width:100%;max-height:200px;resize:vertical}.pf-system-info-module .pf-system-info-description-area .pf-form-field-char-count{display:block;margin-top:10px}.pf-system-info-module .pf-system-info-table{font-size:11px;white-space:nowrap}.pf-sig-table-module .pf-sig-table-clear-button{will-change:opacity, transform;display:none}.pf-sig-table-module .pf-sig-table{font-size:10px}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text{white-space:normal}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-desc-text.editable-empty{border-bottom:none}.pf-sig-table-module .pf-sig-table .pf-editable-description{background-color:#2b2b2b;max-height:50px}.pf-sig-table-module .pf-sig-table .pf-sig-table-edit-name-input{text-transform:uppercase}.pf-sig-table-module .pf-sig-table .pf-editable-filter{color:#63676a;border:none;font-style:normal}.pf-editable-filter-active{min-width:100px}.pf-system-graph-module .pf-system-graph{width:100%;height:100px}.pf-system-route-module .pf-system-route-table{width:100%;font-size:10px}.pf-system-route-module .pf-system-route-table td{text-transform:capitalize}.pf-system-route-module .pf-system-route-table td>.fa{font-size:10px}.pf-system-killboard-module .pf-system-killboard-graph-kills{width:100%;height:100px;position:relative;margin-bottom:30px}.pf-system-killboard-module .pf-system-killboard-list{padding-bottom:10px;border-bottom:1px solid #2b2b2b}.pf-system-killboard-module .pf-system-killboard-list li{margin-left:0;overflow:visible;min-height:50px;will-change:margin-left;-webkit-transition:margin-left 0.12s cubic-bezier(0.3, 0.8, 0.8, 1.7);transition:margin-left 0.12s cubic-bezier(0.3, 0.8, 0.8, 1.7)}.pf-system-killboard-module .pf-system-killboard-list li h5{white-space:nowrap}.pf-system-killboard-module .pf-system-killboard-list li h3{width:120px;display:inline-block}.pf-system-killboard-module .pf-system-killboard-list li .pf-system-killboard-img-corp{margin-right:10px;width:16px}.pf-system-killboard-module .pf-system-killboard-list li .pf-system-killboard-img-ship{width:50px;margin-right:10px;border:1px solid #2b2b2b;transform:translateZ(1px);will-change:border-color;-moz-border-radius:25px;-webkit-border-radius:25px;border-radius:25px;-webkit-transition:border-color 0.12s ease-out;transition:border-color 0.12s ease-out}.pf-system-killboard-module .pf-system-killboard-list li:before{content:"\f054";font-family:FontAwesome;position:absolute;z-index:10;left:-25px;top:15px;color:#477372;opacity:0;will-change:opacity, left;-webkit-transition:all 0.12s ease-out;transition:all 0.12s ease-out}.pf-system-killboard-module .pf-system-killboard-list li:hover{margin-left:20px}.pf-system-killboard-module .pf-system-killboard-list li:hover .pf-system-killboard-img-ship{border-color:#568a89}.pf-system-killboard-module .pf-system-killboard-list li:hover:before{opacity:1;left:-20px}input,select{background-color:#313335;color:#adadad;border:1px solid #63676a;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}input:focus,select:focus{border-color:#568a89}input:-webkit-autofill,select:-webkit-autofill{background-color:#313335 !important;-webkit-box-shadow:0 0 0 50px #313335 inset !important;box-shadow:0 0 0 50px #313335 inset !important;-webkit-text-fill-color:#adadad}input:-webkit-autofill:focus,select:-webkit-autofill:focus{-webkit-box-shadow:0 0 0 50px #313335 inset !important;box-shadow:0 0 0 50px #313335 inset !important;-webkit-text-fill-color:#adadad}input::-webkit-file-upload-button,select::-webkit-file-upload-button{background-color:transparent;border:none;color:#63676a;outline:none}input[disabled]::-moz-placeholder,select[disabled]::-moz-placeholder{color:transparent;opacity:1}input[disabled]:-ms-input-placeholder,select[disabled]:-ms-input-placeholder{color:transparent}input[disabled]::-webkit-input-placeholder,select[disabled]::-webkit-input-placeholder{color:transparent}textarea{min-height:32px;max-height:400px;resize:vertical}fieldset[disabled] .form-control{color:#63676a}fieldset[disabled] .form-control::-moz-placeholder{color:transparent;opacity:1}fieldset[disabled] .form-control:-ms-input-placeholder{color:transparent}fieldset[disabled] .form-control::-webkit-input-placeholder{color:transparent}fieldset[disabled] .input-icon-left .fa-stack i:last-child,fieldset[disabled] .input-icon-right .fa-stack i:last-child{color:#3c3f41}.input-icon-left:not(.input-icon-right) .fa-stack:first-child{left:14px}.input-icon-right:not(.input-icon-left) .fa-stack:first-child{right:14px}.input-icon-left.input-icon-right .fa-stack:first-child{left:14px}.input-icon-left.input-icon-right .fa-stack:nth-child(2){right:14px}.input-icon-left .fa-stack,.input-icon-right .fa-stack{position:absolute;top:4px}.input-icon-left .fa-stack i:first-child,.input-icon-right .fa-stack i:first-child{color:#63676a}.input-icon-left .fa-stack i:last-child,.input-icon-right .fa-stack i:last-child{color:#313335}.btn-fake{border:none;text-align:left;cursor:default;opacity:1 !important;color:#63676a !important;background-color:#3c3f41 !important}.pf-form-dropzone{border:2px dashed #2b2b2b;height:100px;background-color:#353739;text-align:center;font-size:20px;line-height:100px;margin:15px 0;color:#2b2b2b;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;-webkit-transition:color 0.18s ease-out,border-color 0.18s ease-out;transition:color 0.18s ease-out,border-color 0.18s ease-out}.pf-form-dropzone:hover{color:#568a89;border-color:#568a89;cursor:-moz-grabbing;cursor:-webkit-grabbing;cursor:grabbing}.toggle.btn:active{box-shadow:none}.toggle .toggle-group .btn{padding:0px 5px}.pf-icon{display:inline-block}.pf-icon.disabled{opacity:0.5;color:#63676a}.pf-icon-dotlan{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAwpJREFUeNqslE9oXFUUxr9z7333vZk3k3+1JGkyldI44KYKFbSIihbauBOrNkgXdeGqVnAhFnfiQgTFCtJNEQndVDDSFrRBWiGutEYFi2mlhQraP2km02by8ubdd/8cF8WJi+z0bA/nx3cO5/uImfFfSwHAwh9PbNR7EMBLBGoyuA3gKwCzAEAEbLu/gqkXLuLUFzchNhjeAuAXSWJBkXyFwcOSxOOK5FkCMYDnN1QSAoOIQIQnBdGcAP2a+3Kb8dbZYBuCZCcW0Y00io8IiBnP4QMCvSnEvyBbRxMstsuGKXiOJD5ZLlcP586cFJr3S025Z1vtlmY5s90HhpL6dKKi30wIf+W5/xjAvXWmTyzuyLJwvr8eXejYtcOZ6/4ZV2g/Z/rZsqVTu6wpEvJcUK7dLlYDI7zsLB+VUj3cU/L1mSX9yM6+YzzsjmeuOJHEary7pOjulURzoNPs8VN11E4NNnnROHdppcxHtsjKwSRWsgeJRDzvOMyvlMUmpXHAZvLVO5criiQPqzgMMeOd7LpuRqk/UBsPr+fGHOw68z4xobfO9ocIHXSQGzuhpECwdArAcRHxRQbWQAAJ3mdzqYnoZwC7looOCl+uQ9q3gIg1pKTbITCE4jEA37GjfhD2AgA7WpBRKAE0AviaFhKSxDqkdcuM9Im0OZCk10wRrug+/2H/RPGZL+mYzSTKVfl7bax8rD5unzEFb45IfTqcDqbBYqx3k92Tm8bjKmZil3xbkfGurjGt2hh/RIRDZkUdkjqg3jA7g+DzocRbg2maO8vZ5hG9F8B1YmbcyXfjdttO+hJnhcBky6zMd71t6YS8lDjHAROl4e0I9PaArr03OlQzP1y4Ozc0GO156tHv7ym5sViCCLMQeINIzN6XDBzJbFcYa/e5MjQF0Zm6VDPVOK5pEfHqmr3a2JrsSVOx/rFEPRsc9RxuEuhkf1R916sw7REuC9CIJPlNAO9w7E+zw3P1usQ/CaA2MODnDP7Ssn+NQC8KiKcBFI79jwCmAFwiArxfjxD6P/Lk7wEA9Dls2LsiUxoAAAAASUVORK5CYII=');width:17px;height:17px;opacity:0.8;margin:-5px 0px 0 10px}.pf-icon-wormhol-es{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAYAAAA7bUf6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA5xJREFUeNp8lMtrXGUAxc/33e++Z+ZO5pHJZJK0STSx6UMqYrFYqF1I42NRFxVK3dhF3QiCLkQXbrpzoeBOi0iRbHQhWqEIohJRtAmW1tKaZiZSJ4+ZZObOnZl779zHdz8XkYJIe/6Aw+Fwfoc8e3IR95MggBpJCBk/vJ7rPy3HUlONaT89UDa1kC0FMudEAAwPkJQQyJzmlqYabw3UKGO6ahM0UdtGWDlQL3w8ZpuX+mr0XxMCQNxLIZD2FFydbL7TV2Jj/tres6HCB5QT9eZY69yfZfulim1epkCb3osdS5oSSWU1liwjZLTQM1DP9V/csrxDR6qlCwM1bvly7Ppq3N6/nv+IcRKsljpn9JCBCSJg+Qpqhd752oj9nDpQtghBONzVq77Op7K+Uiv29V93Uj6oIOBUgHHijNupK9Wic3q6aX1OjZChZQYnboy1To2101+XHePKiGMs3y7Zj0oJZRqXjWqh83wqkEEAUAFwKvStjP/4kKutEMBlRJDS8p7t1yda6cWDdwsfemoEPWTIeMrt1RHnVStUozsjzgtlJ3WLcepkPMX+fc/Oa7YRzMzfmDgzkHmfXa+0zxNB4sfuFi+0UgMAgKdwDPf079fy3WMRFftKrsl+e2jzDRZLAhCinQqmn1wrvQdgg1MBaoRsiyVEhIyb9/YBAZZQUAE3YIl1sFHcLPqmXwgML8t1cbQ2+mmup37jqhGIAOhsw1pIiKArw51Xij0dWizB8hXUs/0Tns6fKnf12rcPr031lCDnqIN82/BKm5a7TwCMgOzuaW7qXEgF1W9W2qclQHPUeDKS+eS1idbZyY7V2cj2CiVb/+7A34UPTF9etlx15Xpl52UjkAc5T/sjlhIwV4kxahtfekp2ej3XP55wiSdypI130k1XDSkLSXX/Rv6Trh5yJmg772h3xtuZY7VS99S4nfoKMjwyf3IRBIAZMCQEOpcSE4JIXBJzP83U336iWn5XD9nPAeMgAIgAtIiN/jhX/2xmc+jisGMsUPJvma4aw1diP5KSnVhKGlpIfzAHytZf+d4zWiTtIkEEjFBGM+MfjYlAypfXEiJA/0cugIQIcCrE3m3ri3quf7yvRofTA1nKuhpimjyyNNl4c3ZzaCHrqb9EjN+fYl/hqNjm5UbWnV+cq78/1DVWFU7c9Zx7aKKdujrdtC7aZgAiCMiD/kRKCADMbmf8IwPG9UiOs5RTMtW0LiVUbHC6y/w/AwBZTrZC1ec/kgAAAABJRU5ErkJggg==');width:17px;height:17px;opacity:0.8;margin:-5px 0px 0 10px}.modal-content h2{font-family:"Oxygen","Helvetica Neue",Helvetica,Arial,sans-serif;letter-spacing:0px;font-size:14px;margin:20px 0;line-height:normal}.modal-content h2.pf-dynamic-area,.modal-content h4.pf-dynamic-area{min-height:0;margin:0 0 10px 0}.modal-content h2.pf-dynamic-area>img,.modal-content h4.pf-dynamic-area>img{margin:-10px 5px -10px -10px;width:35px}.modal-content h2[data-toggle="collapse"],.modal-content h4[data-toggle="collapse"]{cursor:pointer}.modal-content h2[data-toggle="collapse"]:hover:after,.modal-content h4[data-toggle="collapse"]:hover:after{color:#e28a0d !important}.modal-content h2[data-toggle="collapse"]:after,.modal-content h4[data-toggle="collapse"]:after{content:"\f078";font-family:FontAwesome;font-style:normal;font-weight:normal;font-size:13px;padding-right:10px;position:absolute;color:#e28a0d;top:10px;right:6px}.modal-content h2[data-toggle="collapse"].collapsed:after,.modal-content h4[data-toggle="collapse"].collapsed:after{top:13px;right:5px;color:#63676a}.modal-content .dataTables_wrapper+.alert{margin-top:10px}.modal-content .dataTable,.modal-content .table{font-size:10px;font-family:"Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif}.modal-content hr{margin:5px 0 15px 0;border-color:#63676a}.modal-content .well{margin-bottom:0}.modal-content .well .list-inline{margin-bottom:0}.modal-content .pf-wizard-navigation{margin:0}.modal-content .pf-wizard-navigation li:not(:last-child):before{border-top:1px solid #63676a;content:"";display:block;font-size:0;overflow:hidden;position:relative;top:12px;left:71px;right:1px;width:100%}.modal-content .pf-wizard-navigation li.finished:before{-moz-border-image:-moz-linear-gradient(left, #375959,#375959) 1 1%;-moz-border-image:linear-gradient(to right, #375959,#375959) 1 1%;-o-border-image:linear-gradient(to right, #375959,#375959) 1 1%;-webkit-border-image:-webkit-linear-gradient(left, #375959,#375959) 1 1%;-webkit-border-image:linear-gradient(to right, #375959,#375959) 1 1%;border-image:-moz-linear-gradient(left, #375959,#375959) 1 1%;border-image:-webkit-linear-gradient(left, #375959,#375959) 1 1%;border-image:linear-gradient(to right, #375959,#375959) 1 1%;border-bottom:0}.modal-content .pf-wizard-navigation li.active:before{-moz-border-image:-moz-linear-gradient(left, #4f9e4f,#63676a) 1 1%;-moz-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;-o-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;-webkit-border-image:-webkit-linear-gradient(left, #4f9e4f,#63676a) 1 1%;-webkit-border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;border-image:-moz-linear-gradient(left, #4f9e4f,#63676a) 1 1%;border-image:-webkit-linear-gradient(left, #4f9e4f,#63676a) 1 1%;border-image:linear-gradient(to right, #4f9e4f,#63676a) 1 1%;border-bottom:0}.modal-content .pf-wizard-navigation li>h6{color:#63676a;font-size:11px;margin:5px}.modal-content .pf-wizard-navigation li a:hover+h6{color:#adadad}.modal-content .pf-wizard-navigation li.active a:not(.btn-danger)+h6{color:#adadad}#pf-settings-dialog .form-group .btn-sm,#pf-settings-dialog .form-group .btn-group-sm>.btn{padding:4px 7px 3px}#pf-settings-dialog #pf-dialog-captcha-wrapper{margin:0;padding:3px 0}#pf-map-dialog #pf-map-dialog-character-select,#pf-map-dialog #pf-map-dialog-corporation-select,#pf-map-dialog #pf-map-dialog-alliance-select{width:535px}#pf-route-dialog #pf-route-dialog-map-select{width:300px !important}#pf-shortcuts-dialog td kbd:last-of-type+i{display:none}#pf-manual-scrollspy{position:relative;height:700px;overflow:auto}.pf-system-dialog-select{width:270px !important}#pf-task-dialog .pf-task-dialog-status{min-height:auto}#pf-map-info-logs{margin-bottom:10px}#pf-stats-dialog .pf-dynamic-area{margin-bottom:10px}.pf-credits-dialog .pf-credits-logo-background{overflow:visible;background:url("../img/logo_bg.png");background-size:cover;padding:20px;margin-bottom:20px}.pf-credits-dialog #pf-logo-container{width:355px;height:366px;margin:0 auto}.pf-credits-dialog .pf-dynamic-area{min-height:50px}.pf-credits-dialog .dl-horizontal{display:inline-block;width:48%}.pf-credits-dialog .btn{padding:0}.pf-credits-dialog blockquote{font-size:14px}.pf-log-graph{height:100px;width:100%}.timeline{list-style:none;position:relative}.timeline:before{top:0;bottom:0;position:absolute;content:" ";width:1px;left:50%;margin-top:20px;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzRmOWU0ZiIvPjxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNvbG9yPSIjNjM2NzZhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4f9e4f),color-stop(25%, #63676a));background-image:-moz-linear-gradient(top, #4f9e4f,#63676a 25%);background-image:-webkit-linear-gradient(top, #4f9e4f,#63676a 25%);background-image:linear-gradient(to bottom, #4f9e4f,#63676a 25%)}.timeline>li{margin-bottom:20px;position:relative}.timeline>li.timeline-first .timeline-title{color:#4f9e4f}.timeline>li.timeline-first .timeline-badge{background-color:#4f9e4f}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{width:47%;float:left;border:1px solid #313335;padding:8px;position:relative;background-color:#313335;font-size:11px;-webkit-box-shadow:0 4px 10px rgba(0,0,0,0.4);box-shadow:0 4px 10px rgba(0,0,0,0.4);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.timeline>li>.timeline-panel:before{content:" ";position:absolute;top:10px;right:-8px;display:inline-block;border-top:7px solid transparent;border-left:7px solid #63676a;border-right:0 solid #63676a;border-bottom:7px solid transparent}.timeline>li>.timeline-panel:after{content:" ";position:absolute;top:10px;right:-8px;display:inline-block;border-top:7px solid transparent;border-left:7px solid #63676a;border-right:0 solid #63676a;border-bottom:7px solid transparent}.timeline>li>.timeline-badge{color:#2b2b2b;width:22px;height:22px;line-height:22px;text-align:center;position:absolute;top:7px;left:50%;margin-left:-11px;background-color:#63676a;z-index:100;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{border-left-width:0;border-right-width:7px;left:-8px;right:auto}.timeline>li.timeline-inverted>.timeline-panel:after{border-left-width:0;border-right-width:8px;left:-9px;right:auto}.timeline-title{margin-top:0;color:inherit}.timeline-body>hr{display:none}.timeline-body>hr ~ *{display:none}.timeline-body>p,.timeline-body>ul{margin-bottom:0;list-style-type:disc;margin-left:15px}.timeline-body>p+p{margin-top:5px}@media (max-width: 1200px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(100% - 62px)}ul.timeline>li>.timeline-badge{left:29px;margin-left:0;top:6px}ul.timeline>li>.timeline-panel{float:right}ul.timeline>li>.timeline-panel:before{border-left-width:0;border-right-width:7px;left:-8px;right:auto}ul.timeline>li>.timeline-panel:after{border-left-width:0;border-right-width:7px;left:-8px;right:auto}}.ribbon-wrapper{width:72px;height:88px;overflow:hidden;position:absolute;top:-3px;right:-3px;pointer-events:none}.ribbon{font:bold 12px "Oxygen Bold","Helvetica Neue",Helvetica,Arial,sans-serif;color:#2b2b2b;text-align:center;text-shadow:rgba(255,255,255,0.2) 0px 1px 0px;position:relative;padding:3px 0;left:-4px;top:16px;width:99px;-webkit-box-shadow:2px 3px 3px rgba(0,0,0,0.2);box-shadow:2px 3px 3px rgba(0,0,0,0.2);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ribbon:before,.ribbon:after{content:"";border-left:3px solid transparent;border-right:3px solid transparent;position:absolute;bottom:-3px}.ribbon.ribbon-default{color:#adadad;background-color:#353739;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzJkMzAzMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzJhMmIyZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2d3031),color-stop(100%, #2a2b2d));background-image:-moz-linear-gradient(top, #2d3031,#2a2b2d);background-image:-webkit-linear-gradient(top, #2d3031,#2a2b2d);background-image:linear-gradient(to bottom, #2d3031,#2a2b2d)}.ribbon.ribbon-default:before,.ribbon.ribbon-default:after{border-top:3px solid #000}.ribbon.ribbon-green{background-color:#5cb85c;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzUxYjM1MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRhOTQ0YSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #51b351),color-stop(100%, #4a944a));background-image:-moz-linear-gradient(top, #51b351,#4a944a);background-image:-webkit-linear-gradient(top, #51b351,#4a944a);background-image:linear-gradient(to bottom, #51b351,#4a944a)}.ribbon.ribbon-green:before,.ribbon.ribbon-green:after{border-top:3px solid #285028}.ribbon.ribbon-orange{background-color:#e28a0d;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Q0ODEwYyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2I0NmQwYiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #d4810c),color-stop(100%, #b46d0b));background-image:-moz-linear-gradient(top, #d4810c,#b46d0b);background-image:-webkit-linear-gradient(top, #d4810c,#b46d0b);background-image:linear-gradient(to bottom, #d4810c,#b46d0b)}.ribbon.ribbon-orange:before,.ribbon.ribbon-orange:after{border-top:3px solid #6c4107}.ribbon.ribbon-red{background-color:#d9534f;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2M5MzAyYyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2E4MjgyNCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c9302c),color-stop(100%, #a82824));background-image:-moz-linear-gradient(top, #c9302c,#a82824);background-image:-webkit-linear-gradient(top, #c9302c,#a82824);background-image:linear-gradient(to bottom, #c9302c,#a82824)}.ribbon.ribbon-red:before,.ribbon.ribbon-red:after{border-top:3px solid #541412}.ribbon.ribbon-blue{background-color:#428bca;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzM3ODRjNSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzJkNWM4NSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #3784c5),color-stop(100%, #2d5c85));background-image:-moz-linear-gradient(top, #3784c5,#2d5c85);background-image:-webkit-linear-gradient(top, #3784c5,#2d5c85);background-image:linear-gradient(to bottom, #3784c5,#2d5c85)}.ribbon.ribbon-blue:before,.ribbon.ribbon-blue:after{border-top:3px solid #1a344c}.ribbon:before{left:0}.ribbon:after{right:0}.pf-loading-bars-container{position:relative;z-index:4;margin:0 auto;left:5px;right:19px;width:70px;height:50px;list-style:none}.pf-loading-bars-container .pf-loading-bars-loader{position:absolute;z-index:3;margin:0 auto;left:0;right:0;top:50%;margin-top:-19px;width:56px;height:37px;list-style:none}.pf-loading-bars-container .pf-loading-bars-loader li{background-color:#5cb85c;width:6px;height:6px;float:right;margin-right:3px !important;-webkit-box-shadow:0px 12px 6px rgba(0,0,0,0.2);box-shadow:0px 12px 6px rgba(0,0,0,0.2)}.pf-loading-bars-container .pf-loading-bars-loader li:first-child{-webkit-animation:cssload-loadbars 1.75s cubic-bezier(0.645, 0.045, 0.355, 1) infinite 0s;animation:cssload-loadbars 1.75s cubic-bezier(0.645, 0.045, 0.355, 1) infinite 0s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(2){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -0.35s;animation:cssload-loadbars 1.75s ease-in-out infinite -0.35s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(3){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -0.7s;animation:cssload-loadbars 1.75s ease-in-out infinite -0.7s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(4){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.05s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.05s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(5){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.4s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.4s}.pf-loading-bars-container .pf-loading-bars-loader li:nth-child(6){-webkit-animation:cssload-loadbars 1.75s ease-in-out infinite -1.75s;animation:cssload-loadbars 1.75s ease-in-out infinite -1.75s}@-webkit-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@-moz-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@-ms-keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}@keyframes cssload-loadbars{0%{height:6px;margin-top:16px}33%{height:6px;margin-top:16px}66%{height:31px;margin-top:0px}100%{height:6px;margin-top:16px}}.pf-landing-sticky-panel{position:fixed;min-width:100px;border-radius:5px;padding:7px;box-shadow:0 4px 10px rgba(0,0,0,0.4);background-color:rgba(43,43,43,0.7)}.pf-landing-sticky-panel h4{margin:5px 0 10px 0}.pf-landing-sticky-panel ul{margin-bottom:0}.pf-landing-sticky-panel ul li{text-transform:lowercase}#pf-landing-server-panel{top:50px;left:10px}#pf-landing-admin-panel{bottom:10px;right:10px}.youtube{background-position:center;background-repeat:no-repeat;position:relative;display:inline-block;overflow:hidden;transition:all 200ms ease-out;cursor:pointer}.youtube .play{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAERklEQVR4nOWbTWhcVRTHb1IJVoxGtNCNdal2JYJReC6GWuO83PM/59yUS3FRFARdFlwYP1CfiojQWt36sRCUurRIdVFXIn41lAoVdRGrG1M01YpKrWjiYmaSl8ybZJL3cd+YA//NLObd3++eO8x79z5jSq5Gw+8kov0AP8vMR5l1BtBZQM4B8ks75wCdZdYZZj5qLZ4hov2Nht9Z9vhKKSIaB/gI4M4w62KeAO6Mte4lYOq20FxrlqqOibhHmeWbvNC9ZfDX1mLae391aN6limO/gwgvAPJbWeAZuSDingdwXTBw7/0IsyaA/Fkh+KqOkD+YNfHej1QKD+y7iVlOhgLvFqFfNJvNGyuBJ+KDAF8MDd0tgS8y64OlgSdJMsysL4cG7SOHkyQZLhTee7+d2R2rAVy/S+Jd7/32ouBHAP4gNNRGQyTHc/84NhqNywZp5rvjjnnvt21aABFeCQ+RLwAf2hQ8s7sv9OCLk6AHNgQvIrvbfzKCD76g/O6cu7lf/iER/aQGgy448pExZmhdegAPhR9sObFWH1gT3lp7DaA/5bkIgJhZPgsNmz02novj+KqeApj1ubwXWe4kdyeznAgNvTpE/HQmvKqOMeuFogTUVQSRno+iaLRLAJF7uIgL9O4ubgL8aWgB7S44mNX+35YpICUiAvS9sBLkq1WzT+NFffl6AuoiApi6NT37h6sWkBIRZGkQ8YtLgyji6e1mBYTqCEBPG2Naz+0BWQgtoGoRgCzEsd9hAN1X5BfnFZASUfrSAFQNsyZ1FJASUVpHiLinDJG8U2cBZYogkrcNs5waBAGdstbeU9zdqpw0gPwwSAI6VUxHyFlDpOcHUUBBIuYNs14aZAE5RVwyzPr3/0EAEY0TyfGNjBWQvwZ +CTSbehfAH29mrID8bET0+0EUkAd8WYDOmqJ3ecsG30yr9wqRfm6Y+a1BEFDEjHfHvWmY9ck6CygHvBVr8Xhtb4ZE5HZA3y8DvBNA1TjnrmXWf+sioMwZX5V/VHXMGGMMoKdDCxCRvRWBdzKzdHEO+EisilbPyopHYqp6S9UCAsz4iojI7hUDAtyXVQgIDd6KnOoaWNkbI6FaPSuZGyMArsi7MZoloB4zviI/Nhr3X95jltwTRQmoIfgisy5ai+me67OI7fE4nrqjrqfK1t0eby0FPRB6oGVlchL3rgnfrq19RKbVBdhV9IOSwJmfmJi4vi/4ThERitwyCxVAFqydshuCX5awhQ9KtmuIWd8IDZED/nXT77rvVVv6sHRKwjYi91poqP7Dr+Y6JJ1VSZIMA3wkPNy6bX+o8Bcm0sXMdwM8Fxo0A3xORPaWBp6uPXsmbxCRD0NDL0dOANhVCXy6iAjMcjbcrMt3RITKwdMVRdFo+y5yvkL4eWZ+zHt/ZVD4dEVRNGotpst+dZZZH8k86lqn2pIvT/eqrNfn2xuyqYPZ8mv7s8pfn/8Pybm4TIjanscAAAAASUVORK5CYII=") no-repeat center center;background-size:64px 64px;position:absolute;height:100%;width:100%;opacity:.8;filter:alpha(opacity=80);transition:all 0.2s ease-out}.youtube .play:hover{opacity:1;filter:alpha(opacity=100)} diff --git a/public/img/landing/log_1.jpg b/public/img/landing/log_1.jpg new file mode 100644 index 00000000..a02a6496 Binary files /dev/null and b/public/img/landing/log_1.jpg differ diff --git a/public/img/landing/slack_1.jpg b/public/img/landing/slack_1.jpg new file mode 100644 index 00000000..7bf97f52 Binary files /dev/null and b/public/img/landing/slack_1.jpg differ diff --git a/public/img/landing/thumbs/log_1.jpg b/public/img/landing/thumbs/log_1.jpg new file mode 100644 index 00000000..19cf9f4e Binary files /dev/null and b/public/img/landing/thumbs/log_1.jpg differ diff --git a/public/img/landing/thumbs/slack_1.jpg b/public/img/landing/thumbs/slack_1.jpg new file mode 100644 index 00000000..b81dd31a Binary files /dev/null and b/public/img/landing/thumbs/slack_1.jpg differ diff --git a/public/js/v1.2.5/app/render.js b/public/js/v1.2.5/app/render.js deleted file mode 100644 index 66676088..00000000 --- a/public/js/v1.2.5/app/render.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Render controller - */ - -define(['jquery', 'mustache'], function($, Mustache) { - - 'use strict'; - - /** - * init function will be called before and after a new module is loaded - * @param functionName - * @param config - */ - var initModule = function(functionName, config){ - - if( - typeof config.functions === 'object' && - typeof config.functions[functionName] === 'function' - ){ - config.functions[functionName](); - } - }; - - /** - * load a template and render is with Mustache - * @param config - * @param data - */ - var 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 - ){ - - var content = Mustache.render(template, data); - - // display module - switch(config.link){ - case 'prepend': - config.position.prepend(content); - break; - case 'before': - config.position.before(content); - break; - case 'after': - config.position.after(content); - break; - default: - config.position.append(content); - } - } - - // init module function after render - initModule('after', config); - - - }); - }; - - - return { - showModule: showModule - }; -}); \ No newline at end of file diff --git a/public/js/v1.2.5/lib/validator.min.js b/public/js/v1.2.5/lib/validator.min.js deleted file mode 100644 index 9a49ff3d..00000000 --- a/public/js/v1.2.5/lib/validator.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Validator v0.10.1 for Bootstrap 3, by @1000hz - * Copyright 2016 Cina Saffary - * Licensed under http://opensource.org/licenses/MIT - * - * https://github.com/1000hz/bootstrap-validator - */ - -+function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:a.trim(b.val())}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.$element=a(c),this.$inputs=this.$element.find(d.INPUT_SELECTOR),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),e.errors=a.extend({},d.DEFAULTS.errors,e.errors);for(var f in e.custom)if(!e.errors[f])throw new Error("Missing default error message for custom validator: "+f);a.extend(d.VALIDATORS,e.custom),this.$element.attr("novalidate",!0),this.toggleSubmit(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",d.INPUT_SELECTOR,a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.data("match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})})};d.INPUT_SELECTOR=':input:not([type="submit"], button):enabled:visible',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?b.checkValidity():!0},match:function(b){var c=b.data("match");return!b.val()||b.val()===a(c).val()},minlength:function(a){var b=a.data("minlength");return!a.val()||a.val().length>=b}},d.prototype.onInput=function(b){var c=this,d=a(b.target),e="focusout"!==b.type;this.validateInput(d,e).done(function(){c.toggleSubmit()})},d.prototype.validateInput=function(c,d){var e=b(c),f=c.data("bs.validator.previous"),g=c.data("bs.validator.errors");if(f===e)return a.Deferred().resolve();c.data("bs.validator.previous",e),c.is('[type="radio"]')&&(c=this.$element.find('input[name="'+c.attr("name")+'"]'));var h=a.Event("validate.bs.validator",{relatedTarget:c[0]});if(this.$element.trigger(h),!h.isDefaultPrevented()){var i=this;return this.runValidators(c).done(function(b){c.data("bs.validator.errors",b),b.length?d?i.defer(c,i.showErrors):i.showErrors(c):i.clearErrors(c),g&&b.toString()===g.toString()||(h=b.length?a.Event("invalid.bs.validator",{relatedTarget:c[0],detail:b}):a.Event("valid.bs.validator",{relatedTarget:c[0],detail:g}),i.$element.trigger(h)),i.toggleSubmit(),i.$element.trigger(a.Event("validated.bs.validator",{relatedTarget:c[0]}))})}},d.prototype.runValidators=function(c){function e(a){return c.data(a+"-error")||c.data("error")||"native"==a&&c[0].validationMessage||h.errors[a]}var f=[],g=a.Deferred(),h=this.options;return c.data("bs.validator.deferred")&&c.data("bs.validator.deferred").reject(),c.data("bs.validator.deferred",g),a.each(d.VALIDATORS,a.proxy(function(a,d){if((b(c)||c.attr("required"))&&(c.data(a)||"native"==a)&&!d.call(this,c)){var g=e(a);!~f.indexOf(g)&&f.push(g)}},this)),!f.length&&b(c)&&c.data("remote")?this.defer(c,function(){var d={};d[c.attr("name")]=b(c),a.get(c.data("remote"),d).fail(function(a,b,c){f.push(e("remote")||c)}).always(function(){g.resolve(f)})}):g.resolve(f),g.promise()},d.prototype.validate=function(){var b=this;return a.when(this.$inputs.map(function(){return b.validateInput(a(this),!1)})).then(function(){b.toggleSubmit(),b.$btn.hasClass("disabled")&&b.focusError()}),this},d.prototype.focusError=function(){if(this.options.focus){var b=a(".has-error:first :input");a(document.body).animate({scrollTop:b.offset().top-d.FOCUS_OFFSET},250),b.focus()}},d.prototype.showErrors=function(b){var c=this.options.html?"html":"text",d=b.data("bs.validator.errors"),e=b.closest(".form-group"),f=e.find(".help-block.with-errors"),g=e.find(".form-control-feedback");d.length&&(d=a("
      ").addClass("list-unstyled").append(a.map(d,function(b){return a("
    • ")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){return!b(a(this))}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),this.$btn.hasClass("disabled")&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.destroy=function(){return this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator").find(".form-control-feedback").removeClass([this.options.feedback.error,this.options.feedback.success].join(" ")),this.$inputs.off(".bs.validator").removeData(["bs.validator.errors","bs.validator.deferred","bs.validator.previous"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$element.find('input[type="submit"], button[type="submit"]').removeClass("disabled"),this.$element.find(".has-error, .has-danger").removeClass("has-error has-danger"),this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery); \ No newline at end of file diff --git a/public/js/v1.2.5/app.js b/public/js/v1.3.0/app.js similarity index 95% rename from public/js/v1.2.5/app.js rename to public/js/v1.3.0/app.js index 652d7af6..6683161c 100644 --- a/public/js/v1.2.5/app.js +++ b/public/js/v1.3.0/app.js @@ -11,7 +11,7 @@ requirejs.config({ paths: { layout: 'layout', - config: 'app/config', // path for "configuration" files dir + conf: 'app/conf', // path for "config" files dir dialog: 'app/ui/dialog', // path for "dialog" files dir templates: '../../templates', // template dir img: '../../img', // images dir @@ -59,11 +59,13 @@ requirejs.config({ tweenLite: 'lib/TweenLite.min', // datatables // v1.10.12 DataTables - https://datatables.net + 'datatables.loader': './app/datatables.loader', 'datatables.net': 'lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min', 'datatables.net-buttons': 'lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min', 'datatables.net-buttons-html': 'lib/datatables/Buttons-1.2.1/js/buttons.html5.min', 'datatables.net-responsive': 'lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min', 'datatables.net-select': 'lib/datatables/Select-1.2.0/js/dataTables.select.min', + 'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis', // notification plugin pnotify: 'lib/pnotify/pnotify', // v3.0.0 PNotify - notification core file - https://sciactive.com/pnotify/ @@ -94,6 +96,9 @@ requirejs.config({ customScrollbar: { deps: ['jquery', 'mousewheel'] }, + 'datatables.loader': { + deps: ['jquery'] + }, 'datatables.net': { deps: ['jquery'] }, @@ -109,6 +114,9 @@ requirejs.config({ 'datatables.net-select': { deps: ['datatables.net'] }, + 'datatables.plugins.render.ellipsis': { + deps: ['datatables.net'] + }, xEditable: { deps: ['bootstrap'] }, diff --git a/public/js/v1.2.5/app/admin.js b/public/js/v1.3.0/app/admin.js similarity index 87% rename from public/js/v1.2.5/app/admin.js rename to public/js/v1.3.0/app/admin.js index aecb732a..18a7c531 100644 --- a/public/js/v1.2.5/app/admin.js +++ b/public/js/v1.3.0/app/admin.js @@ -6,11 +6,7 @@ define([ 'jquery', 'app/init', 'app/util', - 'datatables.net', - 'datatables.net-buttons', - 'datatables.net-buttons-html', - 'datatables.net-responsive', - 'datatables.net-select' + 'datatables.loader' ], function($, Init, Util) { 'use strict'; diff --git a/public/js/v1.2.5/app/config/signature_type.js b/public/js/v1.3.0/app/conf/signature_type.js similarity index 100% rename from public/js/v1.2.5/app/config/signature_type.js rename to public/js/v1.3.0/app/conf/signature_type.js diff --git a/public/js/v1.2.5/app/config/system_effect.js b/public/js/v1.3.0/app/conf/system_effect.js similarity index 100% rename from public/js/v1.2.5/app/config/system_effect.js rename to public/js/v1.3.0/app/conf/system_effect.js diff --git a/public/js/v1.2.5/app/counter.js b/public/js/v1.3.0/app/counter.js similarity index 100% rename from public/js/v1.2.5/app/counter.js rename to public/js/v1.3.0/app/counter.js diff --git a/public/js/v1.3.0/app/datatables.loader.js b/public/js/v1.3.0/app/datatables.loader.js new file mode 100644 index 00000000..82f451bc --- /dev/null +++ b/public/js/v1.3.0/app/datatables.loader.js @@ -0,0 +1,11 @@ +define([ + 'datatables.net', + 'datatables.net-buttons', + 'datatables.net-buttons-html', + 'datatables.net-responsive', + 'datatables.net-select' +], (a, b) => { + 'use strict'; + + // all Datatables stuff is available... +}); \ No newline at end of file diff --git a/public/js/v1.2.5/app/init.js b/public/js/v1.3.0/app/init.js similarity index 98% rename from public/js/v1.2.5/app/init.js rename to public/js/v1.3.0/app/init.js index 0b0e6f8f..79360978 100644 --- a/public/js/v1.2.5/app/init.js +++ b/public/js/v1.3.0/app/init.js @@ -31,6 +31,7 @@ define(['jquery'], function($) { deleteMap: 'api/map/delete', // ajax URL - delete map importMap: 'api/map/import', // ajax URL - import map getMapConnectionData: 'api/map/getConnectionData', // ajax URL - get connection data + getMapLogData: 'api/map/getLogData', // ajax URL - get logs data // system API searchSystem: 'api/system/search', // ajax URL - search system by name saveSystem: 'api/system/save', // ajax URL - saves system to map @@ -38,6 +39,7 @@ define(['jquery'], function($) { getSystemGraphData: 'api/system/graphData', // ajax URL - get all system graph data getConstellationData: 'api/system/constellationData', // ajax URL - get system constellation data setDestination: 'api/system/setDestination', // ajax URL - set destination + pokeRally: 'api/system/pokeRally', // ajax URL - send rally point pokes // connection API saveConnection: 'api/connection/save', // ajax URL - save new connection to map deleteConnection: 'api/connection/delete', // ajax URL - delete connection from map @@ -52,10 +54,6 @@ define(['jquery'], function($) { // GitHub API gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub }, - url: { - ccpImageServer: '//image.eveonline.com/', // CCP image Server - zKillboard: '//zkillboard.com/api/' // killboard api - }, breakpoints: [ { name: 'desktop', width: Infinity }, { name: 'tablet', width: 1200 }, diff --git a/public/js/v1.2.5/app/key.js b/public/js/v1.3.0/app/key.js similarity index 91% rename from public/js/v1.2.5/app/key.js rename to public/js/v1.3.0/app/key.js index 01a04cdb..5e264c3b 100644 --- a/public/js/v1.2.5/app/key.js +++ b/public/js/v1.3.0/app/key.js @@ -63,7 +63,7 @@ define([ }; /** - * enables some console.log() information + * enables some debug output in console * @type {boolean} */ let debug = false; @@ -297,20 +297,29 @@ define([ // global dom remove listener ------------------------------------------------------------------- // -> check whether the removed element had an event listener active and removes them. - document.body.addEventListener ('DOMNodeRemoved', function(e){ - if(typeof e.target.getAttribute === 'function'){ - let eventNames = e.target.getAttribute(dataKeyEvents); - if(eventNames){ - eventNames.split(',').forEach((event) => { - let index = allEvents[event].elements.indexOf(e.target); - if(index > -1){ - // remove element from event list - allEvents[event].elements.splice(index, 1); + new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if(mutation.type === 'childList'){ + for (let i = 0; i < mutation.removedNodes.length; i++){ + let removedNode = mutation.removedNodes[i]; + if(typeof removedNode.getAttribute === 'function'){ + let eventNames = removedNode.getAttribute(dataKeyEvents); + if(eventNames){ + let events = eventNames.split(','); + for(let j = 0; i < events.length; j++){ + let event = events[j]; + let index = allEvents[event].elements.indexOf(removedNode); + if(index > -1){ + // remove element from event list + allEvents[event].elements.splice(index, 1); + } + } + } } - }); + } } - } - }, false); + }); + }).observe(document.body, { childList: true, subtree: true }); isInit = true; } diff --git a/public/js/v1.2.5/app/logging.js b/public/js/v1.3.0/app/logging.js similarity index 99% rename from public/js/v1.2.5/app/logging.js rename to public/js/v1.3.0/app/logging.js index 61f62e94..0cfc5cf6 100644 --- a/public/js/v1.2.5/app/logging.js +++ b/public/js/v1.3.0/app/logging.js @@ -80,7 +80,7 @@ define([ let showDialog = function(){ // dialog content - requirejs(['text!templates/dialog/task_manager.html', 'mustache'], function(templateTaskManagerDialog, Mustache) { + requirejs(['text!templates/dialog/task_manager.html', 'mustache', 'datatables.loader'], function(templateTaskManagerDialog, Mustache) { let data = { id: config.taskDialogId, dialogDynamicAreaClass: config.dialogDynamicAreaClass, diff --git a/public/js/v1.2.5/app/login.js b/public/js/v1.3.0/app/login.js similarity index 98% rename from public/js/v1.2.5/app/login.js rename to public/js/v1.3.0/app/login.js index 2d189bb4..30ed0997 100644 --- a/public/js/v1.2.5/app/login.js +++ b/public/js/v1.3.0/app/login.js @@ -676,7 +676,8 @@ define([ dataType: 'json', context: { cookieName: requestData.cookie, - characterElement: characterElement + characterElement: characterElement, + browserTabId: Util.getBrowserTabId() } }).done(function(responseData, textStatus, request){ this.characterElement.hideLoadingAnimation(); @@ -698,9 +699,11 @@ define([ let data = { link: this.characterElement.data('href'), cookieName: this.cookieName, + browserTabId: this.browserTabId, character: responseData.character, authLabel: getCharacterAuthLabel(responseData.character.authStatus), - authOK: responseData.character.authStatus === 'OK' + authOK: responseData.character.authStatus === 'OK', + hasActiveSession: responseData.character.hasActiveSession === true }; let content = Mustache.render(template, data); @@ -766,6 +769,12 @@ define([ * main init "landing" page */ $(function(){ + // clear sessionStorage + Util.clearSessionStorage(); + + // set default AJAX config + Util.ajaxSetup(); + // set Dialog default config Util.initDefaultBootboxConfig(); diff --git a/public/js/v1.2.5/app/map/contextmenu.js b/public/js/v1.3.0/app/map/contextmenu.js similarity index 100% rename from public/js/v1.2.5/app/map/contextmenu.js rename to public/js/v1.3.0/app/map/contextmenu.js diff --git a/public/js/v1.2.5/app/map/local.js b/public/js/v1.3.0/app/map/local.js similarity index 55% rename from public/js/v1.2.5/app/map/local.js rename to public/js/v1.3.0/app/map/local.js index 29bdf747..3468c6fd 100644 --- a/public/js/v1.2.5/app/map/local.js +++ b/public/js/v1.3.0/app/map/local.js @@ -28,9 +28,9 @@ define([ overlayLocalJumpsClass: 'pf-map-overlay-local-jumps', // class for jump distance for table results // dataTable - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells - tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells - tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells + tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells + tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) // toolbar toolbarClass: 'pf-map-overlay-toolbar', // class for toolbar - content @@ -288,236 +288,240 @@ define([ * @returns {*} */ $.fn.initLocalOverlay = function(mapId){ - return this.each(function(){ - let parentElement = $(this); + let parentElements = $(this); - let overlay = $('
      ', { - class: [config.overlayClass, config.overlayLocalClass].join(' ') - }); + require(['datatables.loader'], () => { + parentElements.each(function(){ + let parentElement = $(this); - let content = $('
      ', { - class: [ 'text-right', config.overlayLocalContentClass].join(' ') - }); + let overlay = $('
      ', { + class: [config.overlayClass, config.overlayLocalClass].join(' ') + }); - // crate new route table - let table = $('
', { - class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ') - }); + let content = $('
', { + class: [ 'text-right', config.overlayLocalContentClass].join(' ') + }); - let overlayMain = $('
', { - text: '', - class: config.overlayLocalMainClass - }).append( - $('', { - class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ') - }), - $('', { - class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '), - text: 0 - }), - $('
', { - class: config.overlayLocalJumpsClass + // crate new route table + let table = $('
', { + class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ') + }); + + let overlayMain = $('
', { + text: '', + class: config.overlayLocalMainClass + }).append( + $('', { + class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ') + }), + $('', { + class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '), + text: 0 + }), + $('
', { + class: config.overlayLocalJumpsClass + }).append( + $('', { + class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '), + text: MapUtil.config.defaultLocalJumpRadius + }).attr('title', 'jumps') + ) + ); + + let headline = $('
', { + class: config.overlayLocalHeadlineClass }).append( $('', { - class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '), - text: MapUtil.config.defaultLocalJumpRadius - }).attr('title', 'jumps') - ) - ); + html: 'Nearby   ', + class: 'pull-left' + }), + $(''), + $(''), + $('', { + class: ['badge', ' txt-color', 'txt-color-red'].join(' '), + text: 0 + }) + ); - let headline = $('
', { - class: config.overlayLocalHeadlineClass - }).append( - $('', { - html: 'Nearby   ', - class: 'pull-left' - }), - $(''), - $(''), - $('', { - class: ['badge', ' txt-color', 'txt-color-red'].join(' '), - text: 0 - }) - ); + content.append(headline); + content.append(table); + // toolbar not used for now + // content.append(initToolbar()); - content.append(headline); - content.append(table); - // toolbar not used for now - // content.append(initToolbar()); + overlay.append(overlayMain); + overlay.append(content); - overlay.append(overlayMain); - overlay.append(content); + // set observer + setOverlayObserver(overlay, mapId); - // set observer - setOverlayObserver(overlay, mapId); + parentElement.append(overlay); - parentElement.append(overlay); + // init local table --------------------------------------------------------------------------------------- - // init local table --------------------------------------------------------------------------------------- + table.on('draw.dt', function(e, settings){ + // init table tooltips + $(this).find('td').initTooltips({ + container: 'body', + placement: 'left' + }); - table.on('draw.dt', function(e, settings){ - // init table tooltips - $(this).find('td').initTooltips({ - container: 'body', - placement: 'left' - }); - - // hide pagination in case of only one page - let paginationElement = overlay.find('.dataTables_paginate'); - let pageElements = paginationElement.find('span .paginate_button'); - if(pageElements.length <= 1){ - paginationElement.hide(); - }else{ - paginationElement.show(); - } - }); - - // table init complete - table.on( 'init.dt', function (){ - // init table head tooltips - $(this).initTooltips({ - container: 'body', - placement: 'top' - }); - }); - - let localTable = table.DataTable( { - pageLength: 13, // hint: if pagination visible => we need space to show it - paging: true, - lengthChange: false, - ordering: true, - order: [ 0, 'asc' ], - info: false, - searching: false, - hover: false, - autoWidth: false, - rowId: function(rowData) { - return 'pf-local-row_' + rowData.id; // characterId - }, - language: { - emptyTable: 'You are alone' - }, - columnDefs: [ - { - targets: 0, - orderable: true, - title: ' ', - width: '1px', - className: ['pf-help-default', 'text-center'].join(' '), - data: 'jumps', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - if(value === 0){ - value = ''; - } - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.system.name'); - } - },{ - targets: 1, - orderable: false, - title: '', - width: '26px', - className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '), - data: 'log.ship', - render: { - _: function(data, type, row, meta){ - let value = data.typeName; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.ship.typeName'); - } - }, { - targets: 2, - orderable: true, - title: 'ship name', - width: '80px', - data: 'log.ship', - render: { - _: function(data, type, row, meta){ - let value = data.name; - if(type === 'display'){ - value = '
' + data.name + '
'; - } - return value; - }, - sort: 'name' - } - },{ - targets: 3, - orderable: true, - title: 'pilot', - data: 'name', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - value = '
' + data + '
'; - } - return value; - } - } - },{ - targets: 4, - orderable: false, - title: '', - width: '10px', - className: ['pf-help-default'].join(' '), - data: 'log.station', - render: { - _: function(data, type, row, meta){ - let value = ''; - if( - type === 'display' && - data.id - ){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - let api = this.DataTable(); - initCellTooltip(api, cell, 'log.station.name'); - } - },{ - targets: 5, - orderable: false, - title: '', - width: '10px', - className: [config.tableActionCellClass].join(' '), - data: 'id', - render: { - _: function(data, type, row, meta){ - let value = data; - if(type === 'display'){ - value = ''; - } - return value; - } - }, - createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ - // open character information window (ingame) - $(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){ - let cellData = e.data.tableApi.cell(this).data(); - Util.openIngameWindow(e.data.cellData); - }); - } + // hide pagination in case of only one page + let paginationElement = overlay.find('.dataTables_paginate'); + let pageElements = paginationElement.find('span .paginate_button'); + if(pageElements.length <= 1){ + paginationElement.hide(); + }else{ + paginationElement.show(); } - ] + }); + + // table init complete + table.on( 'init.dt', function (){ + // init table head tooltips + $(this).initTooltips({ + container: 'body', + placement: 'top' + }); + }); + + let localTable = table.DataTable( { + pageLength: 13, // hint: if pagination visible => we need space to show it + paging: true, + lengthChange: false, + ordering: true, + order: [ 0, 'asc' ], + info: false, + searching: false, + hover: false, + autoWidth: false, + rowId: function(rowData) { + return 'pf-local-row_' + rowData.id; // characterId + }, + language: { + emptyTable: 'You are alone' + }, + columnDefs: [ + { + targets: 0, + orderable: true, + title: ' ', + width: '1px', + className: ['pf-help-default', 'text-center'].join(' '), + data: 'jumps', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + if(value === 0){ + value = ''; + } + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.system.name'); + } + },{ + targets: 1, + orderable: false, + title: '', + width: '26px', + className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '), + data: 'log.ship', + render: { + _: function(data, type, row, meta){ + let value = data.typeName; + if(type === 'display'){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.ship.typeName'); + } + }, { + targets: 2, + orderable: true, + title: 'ship name', + width: '80px', + data: 'log.ship', + render: { + _: function(data, type, row, meta){ + let value = data.name; + if(type === 'display'){ + value = '
' + data.name + '
'; + } + return value; + }, + sort: 'name' + } + },{ + targets: 3, + orderable: true, + title: 'pilot', + data: 'name', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = '
' + data + '
'; + } + return value; + } + } + },{ + targets: 4, + orderable: false, + title: '', + width: '10px', + className: ['pf-help-default'].join(' '), + data: 'log.station', + render: { + _: function(data, type, row, meta){ + let value = ''; + if( + type === 'display' && + data.id + ){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let api = this.DataTable(); + initCellTooltip(api, cell, 'log.station.name'); + } + },{ + targets: 5, + orderable: false, + title: '', + width: '10px', + className: [config.tableCellActionClass].join(' '), + data: 'id', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // open character information window (ingame) + $(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){ + let cellData = e.data.tableApi.cell(this).data(); + Util.openIngameWindow(e.data.cellData); + }); + } + } + ] + }); }); }); }; diff --git a/public/js/v1.2.5/app/map/magnetizing.js b/public/js/v1.3.0/app/map/magnetizing.js similarity index 100% rename from public/js/v1.2.5/app/map/magnetizing.js rename to public/js/v1.3.0/app/map/magnetizing.js diff --git a/public/js/v1.2.5/app/map/map.js b/public/js/v1.3.0/app/map/map.js similarity index 96% rename from public/js/v1.2.5/app/map/map.js rename to public/js/v1.3.0/app/map/map.js index 687dfba3..8cd97831 100644 --- a/public/js/v1.2.5/app/map/map.js +++ b/public/js/v1.3.0/app/map/map.js @@ -945,14 +945,14 @@ define([ mapContainer = $(mapContainer); // add additional information for this map - if(mapContainer.data('updated') !== mapConfig.config.updated){ + if(mapContainer.data('updated') !== mapConfig.config.updated.updated){ mapContainer.data('name', mapConfig.config.name); mapContainer.data('scopeId', mapConfig.config.scope.id); mapContainer.data('typeId', mapConfig.config.type.id); mapContainer.data('typeName', mapConfig.config.type.name); mapContainer.data('icon', mapConfig.config.icon); - mapContainer.data('created', mapConfig.config.created); - mapContainer.data('updated', mapConfig.config.updated); + mapContainer.data('created', mapConfig.config.created.created); + mapContainer.data('updated', mapConfig.config.updated.updated); } // get map data @@ -1320,11 +1320,10 @@ define([ * @returns {boolean} */ let isValidSystem = function(systemData){ - let isValid = true; if( - ! systemData.hasOwnProperty('name') || + !systemData.hasOwnProperty('name') || systemData.name.length === 0 ){ return false; @@ -1382,37 +1381,51 @@ define([ /** * save a new system and add it to the map - * @param map * @param requestData - * @param sourceSystem - * @param callback + * @param context */ - let saveSystem = function(map, requestData, sourceSystem, callback){ + let saveSystem = function(requestData, context){ $.ajax({ type: 'POST', url: Init.path.saveSystem, data: requestData, dataType: 'json', - context: { - map: map, - sourceSystem: sourceSystem + context: context + }).done(function(responseData){ + let newSystemData = responseData.systemData; + + if( !$.isEmptyObject(newSystemData) ){ + Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); + + // draw new system to map + drawSystem(this.map, newSystemData, this.sourceSystem); + + // re/arrange systems (prevent overlapping) + MagnetizerWrapper.setElements(this.map); + + if(this.onSuccess){ + this.onSuccess(); + } } - }).done(function(newSystemData){ - Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'}); - // draw new system to map - drawSystem(this.map, newSystemData, this.sourceSystem); - - // re/arrange systems (prevent overlapping) - MagnetizerWrapper.setElements(this.map); - - if(callback){ - callback(); + // show errors + if( + responseData.error && + responseData.error.length > 0 + ){ + for(let i = 0; i < responseData.error.length; i++){ + let error = responseData.error[i]; + Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); + } } }).fail(function( jqXHR, status, error) { let reason = status + ' ' + error; Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); + }).always(function(){ + if(this.onAlways){ + this.onAlways(this); + } }); }; @@ -1542,8 +1555,18 @@ define([ } }; - saveSystem(map, requestData, sourceSystem, function(){ - bootbox.hideAll(); + this.find('.modal-content').showLoadingAnimation(); + + saveSystem(requestData, { + map: map, + sourceSystem: sourceSystem, + systemDialog: this, + onSuccess: () => { + bootbox.hideAll(); + }, + onAlways: (context) => { + context.systemDialog.find('.modal-content').hideLoadingAnimation(); + } }); return false; } @@ -1694,7 +1717,8 @@ define([ mapId: mapId, oldConnectionData: connectionData } - }).done(function(newConnectionData){ + }).done(function(responseData){ + let newConnectionData = responseData.connectionData; if( !$.isEmptyObject(newConnectionData) ){ let updateCon = false; @@ -1739,6 +1763,16 @@ define([ this.map.detach(this.connection, {fireEvent: false}); } + // show errors + if( + responseData.error && + responseData.error.length > 0 + ){ + for(let i = 0; i < responseData.error.length; i++){ + let error = responseData.error[i]; + Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type}); + } + } }).fail(function( jqXHR, status, error) { // remove this connection from map this.map.detach(this.connection, {fireEvent: false}); @@ -1826,7 +1860,6 @@ define([ let moduleData = { id: config.mapContextMenuId, items: [ - {icon: 'fa-street-view', action: 'info', text: 'information'}, {icon: 'fa-plus', action: 'add_system', text: 'add system'}, {icon: 'fa-object-ungroup', action: 'select_all', text: 'select all'}, {icon: 'fa-filter', action: 'filter_scope', text: 'filter scope', subitems: [ @@ -1834,6 +1867,10 @@ define([ {subIcon: '', subAction: 'filter_stargate', subText: 'stargate'}, {subIcon: '', subAction: 'filter_jumpbridge', subText: 'jumpbridge'} ]}, + {icon: 'fa-sitemap', action: 'map', text: 'map', subitems: [ + {subIcon: 'fa-edit', subAction: 'map_edit', subText: 'edit map'}, + {subIcon: 'fa-street-view', subAction: 'map_info', subText: 'map info'}, + ]}, {divider: true, action: 'delete_systems'}, {icon: 'fa-trash', action: 'delete_systems', text: 'delete systems'} ] @@ -1905,7 +1942,7 @@ define([ items: [ {icon: 'fa-plus', action: 'add_system', text: 'add system'}, {icon: 'fa-lock', action: 'lock_system', text: 'lock system'}, - {icon: 'fa-users', action: 'set_rally', text: 'set rally point'}, + {icon: 'fa-volume-up', action: 'set_rally', text: 'set rally point'}, {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'}, @@ -2731,8 +2768,12 @@ define([ let selectedSystems = currentMapElement.getSelectedSystems(); $.fn.showDeleteSystemDialog(currentMap, selectedSystems); break; - case 'info': - // open map info dialog + case 'map_edit': + // open map edit dialog tab + $(document).triggerMenuEvent('ShowMapSettings', {tab: 'edit'}); + break; + case 'map_info': + // open map info dialog tab $(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'}); break; @@ -2893,37 +2934,38 @@ define([ let mapElement = $(this); let mapOverlay = mapElement.getMapOverlay('local'); - let currentCharacterLog = Util.getCurrentCharacterLog(); - let currentMapData = Util.getCurrentMapData(userData.config.id); - let clearLocal = true; + if(userData && userData.config && userData.config.id){ + let currentMapData = Util.getCurrentMapData(userData.config.id); + let currentCharacterLog = Util.getCurrentCharacterLog(); + let clearLocal = true; - if( - currentMapData && - currentCharacterLog && - currentCharacterLog.system - ){ - let currentSystemData = currentMapData.data.systems.filter(function (system) { - return system.systemId === currentCharacterLog.system.id; - }); + if( + currentMapData && + currentCharacterLog && + currentCharacterLog.system + ){ + let currentSystemData = currentMapData.data.systems.filter(function (system) { + return system.systemId === currentCharacterLog.system.id; + }); - if(currentSystemData.length){ - // current user system is on this map - currentSystemData = currentSystemData[0]; + if(currentSystemData.length){ + // current user system is on this map + currentSystemData = currentSystemData[0]; - // check for active users "nearby" (x jumps radius) - let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius); - let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems); + // check for active users "nearby" (x jumps radius) + let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius); + let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems); - // update "local" table in overlay - mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData); - clearLocal = false; + // update "local" table in overlay + mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData); + clearLocal = false; + } + } + + if(clearLocal){ + mapOverlay.clearLocalTable(); } } - - if(clearLocal){ - mapOverlay.clearLocalTable(); - } - }); }; diff --git a/public/js/v1.2.5/app/map/overlay.js b/public/js/v1.3.0/app/map/overlay.js similarity index 100% rename from public/js/v1.2.5/app/map/overlay.js rename to public/js/v1.3.0/app/map/overlay.js diff --git a/public/js/v1.2.5/app/map/scrollbar.js b/public/js/v1.3.0/app/map/scrollbar.js similarity index 100% rename from public/js/v1.2.5/app/map/scrollbar.js rename to public/js/v1.3.0/app/map/scrollbar.js diff --git a/public/js/v1.2.5/app/map/system.js b/public/js/v1.3.0/app/map/system.js similarity index 65% rename from public/js/v1.2.5/app/map/system.js rename to public/js/v1.3.0/app/map/system.js index 2b149f93..b5dac0ff 100644 --- a/public/js/v1.2.5/app/map/system.js +++ b/public/js/v1.3.0/app/map/system.js @@ -18,7 +18,19 @@ define([ y: 0 }, - systemActiveClass: 'pf-system-active' // class for an active system in a map + systemActiveClass: 'pf-system-active', // class for an active system in a map + + dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog + + dialogRallyPokeDesktopId: 'pf-rally-dialog-poke-desktop', // id for "desktop" poke checkbox + dialogRallyPokeSlackId: 'pf-rally-dialog-poke-slack', // id for "Slack" poke checkbox + dialogRallyPokeMailId: 'pf-rally-dialog-poke-mail', // id for "mail" poke checkbox + dialogRallyMessageId: 'pf-rally-dialog-message', // id for "message" textarea + + dialogRallyMessageDefault: '' + + 'I need some help!\n\n' + + '- Potential PvP options around\n' + + '- DPS and Logistic ships needed' }; /** @@ -26,41 +38,105 @@ define([ * @param system */ $.fn.showRallyPointDialog = (system) => { + let mapData = Util.getCurrentMapData(system.data('mapid')); + requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) { + + let setCheckboxObserver = (checkboxes) => { + checkboxes.each(function(){ + $(this).on('change', function(){ + // check all others + let allUnchecked = true; + checkboxes.each(function(){ + if(this.checked){ + allUnchecked = false; + } + }); + let textareaElement = $('#' + config.dialogRallyMessageId); + if(allUnchecked){ + textareaElement.prop('disabled', true); + }else{ + textareaElement.prop('disabled', false); + } + }); + }); + }; + + let sendPoke = (requestData, context) => { + // lock dialog + let dialogContent = context.rallyDialog.find('.modal-content'); + dialogContent.showLoadingAnimation(); + + $.ajax({ + type: 'POST', + url: Init.path.pokeRally, + data: requestData, + dataType: 'json', + context: context + }).done(function(data){ + + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': sendPoke', text: reason, type: 'warning'}); + }).always(function(){ + this.rallyDialog.find('.modal-content').hideLoadingAnimation(); + }); + }; + let data = { - notificationStatus: Init.notificationStatus.rallySet + id: config.dialogRallyId, + + dialogRallyPokeDesktopId: config.dialogRallyPokeDesktopId, + dialogRallyPokeSlackId: config.dialogRallyPokeSlackId, + dialogRallyPokeMailId: config.dialogRallyPokeMailId, + dialogRallyMessageId: config.dialogRallyMessageId , + + desktopRallyEnabled: true, + slackRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.slackRally')), + mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')), + dialogRallyMessageDefault: config.dialogRallyMessageDefault, + + systemId: system.data('id') }; let content = Mustache.render(template, data); let rallyDialog = bootbox.dialog({ message: content, - title: 'Set rally point for "' + system.getSystemInfo( ['alias'] ) + '"', + title: 'Set rally point in "' + system.getSystemInfo( ['alias'] ) + '"', buttons: { close: { label: 'cancel', className: 'btn-default' }, - setRallyPoke: { - label: ' set rally and poke', - className: 'btn-primary', - callback: function() { - system.setSystemRally(1, { - poke: true - }); - system.markAsChanged(); - } - }, success: { - label: ' set rally', + label: ' set rally point', className: 'btn-success', callback: function() { - system.setSystemRally(1); + let form = $('#' + config.dialogRallyId).find('form'); + // get form data + let formData = form.getFormValues(); + + // update map + system.setSystemRally(1, { + poke: Boolean(formData.pokeDesktop) + }); system.markAsChanged(); + + // send poke data to server + sendPoke(formData, { + rallyDialog: this + }); } } } }); + + // after modal is shown ================================================================================== + rallyDialog.on('shown.bs.modal', function(e){ + // set event for checkboxes + setCheckboxObserver(rallyDialog.find(':checkbox')); + }); }); }; diff --git a/public/js/v1.2.5/app/map/util.js b/public/js/v1.3.0/app/map/util.js similarity index 100% rename from public/js/v1.2.5/app/map/util.js rename to public/js/v1.3.0/app/map/util.js diff --git a/public/js/v1.2.5/app/map/worker.js b/public/js/v1.3.0/app/map/worker.js similarity index 100% rename from public/js/v1.2.5/app/map/worker.js rename to public/js/v1.3.0/app/map/worker.js diff --git a/public/js/v1.2.5/app/mappage.js b/public/js/v1.3.0/app/mappage.js similarity index 98% rename from public/js/v1.2.5/app/mappage.js rename to public/js/v1.3.0/app/mappage.js index a856c0a6..34a0d29b 100644 --- a/public/js/v1.2.5/app/mappage.js +++ b/public/js/v1.3.0/app/mappage.js @@ -23,6 +23,9 @@ define([ $(() => { Util.initPrototypes(); + // clear sessionStorage + //Util.clearSessionStorage(); + // set default AJAX config Util.ajaxSetup(); @@ -63,7 +66,8 @@ define([ Init.systemType = initData.systemType; Init.characterStatus = initData.characterStatus; Init.routes = initData.routes; - Init.notificationStatus = initData.notificationStatus; + Init.url = initData.url; + Init.slack = initData.slack; Init.routeSearch = initData.routeSearch; Init.programMode = initData.programMode; @@ -165,8 +169,7 @@ define([ if(jqXHR.responseJSON){ // handle JSON - let errorObj = $.parseJSON(jqXHR.responseText); - + let errorObj = jqXHR.responseJSON; if( errorObj.error && errorObj.error.length > 0 @@ -182,7 +185,6 @@ define([ } $(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData}); - }; /** diff --git a/public/js/v1.2.5/app/module_map.js b/public/js/v1.3.0/app/module_map.js similarity index 94% rename from public/js/v1.2.5/app/module_map.js rename to public/js/v1.3.0/app/module_map.js index 59fad3fc..1337d183 100644 --- a/public/js/v1.2.5/app/module_map.js +++ b/public/js/v1.3.0/app/module_map.js @@ -9,12 +9,7 @@ define([ 'app/ui/system_graph', 'app/ui/system_signature', 'app/ui/system_route', - 'app/ui/system_killboard', - 'datatables.net', - 'datatables.net-buttons', - 'datatables.net-buttons-html', - 'datatables.net-responsive', - 'datatables.net-select' + 'app/ui/system_killboard' ], function($, Init, Util, Map, MapUtil) { 'use strict'; @@ -106,29 +101,31 @@ define([ * @param tabContentElement */ let drawSystemModules = function(tabContentElement){ - let currentSystemData = Util.getCurrentSystemData(); + require(['datatables.loader'], () => { + let currentSystemData = Util.getCurrentSystemData(); - // get Table cell for system Info - let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst); - let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond); + // get Table cell for system Info + let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst); + let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond); - // draw system info module - firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData); + // draw system info module + firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system graph module - firstCell.drawSystemGraphModule(currentSystemData.systemData); + // draw system graph module + firstCell.drawSystemGraphModule(currentSystemData.systemData); - // draw signature table module - firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData); + // draw signature table module + firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system routes module - secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData); + // draw system routes module + secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData); - // draw system killboard module - secondCell.drawSystemKillboardModule(currentSystemData.systemData); + // draw system killboard module + secondCell.drawSystemKillboardModule(currentSystemData.systemData); - // set Module Observer - setModuleObserver(); + // set Module Observer + setModuleObserver(); + }); }; /** @@ -295,7 +292,12 @@ define([ let tabElement = $(this); // set "main" data - tabElement.data('map-id', options.id).data('updated', options.updated); + tabElement.data('map-id', options.id); + + // add updated timestamp (not available for "add" tab + if(Util.getObjVal(options, 'updated.updated')){ + tabElement.data('updated', options.updated.updated); + } // change "tab" link tabElement.attr('href', '#' + config.mapTabIdPrefix + options.id); @@ -565,7 +567,7 @@ define([ activeMapIds.push(mapId); // check for map data change and update tab - if(tabMapData.config.updated > tabElement.data('updated')){ + if(tabMapData.config.updated.updated > tabElement.data('updated')){ tabElement.updateTabData(tabMapData.config); } }else{ diff --git a/public/js/v1.2.5/app/notification.js b/public/js/v1.3.0/app/notification.js similarity index 100% rename from public/js/v1.2.5/app/notification.js rename to public/js/v1.3.0/app/notification.js diff --git a/public/js/v1.2.5/app/page.js b/public/js/v1.3.0/app/page.js similarity index 96% rename from public/js/v1.2.5/app/page.js rename to public/js/v1.3.0/app/page.js index 1a7613af..bfc863f9 100644 --- a/public/js/v1.2.5/app/page.js +++ b/public/js/v1.3.0/app/page.js @@ -198,8 +198,7 @@ define([ getMenuHeadline('Information') ).append( $('
', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Statistics').prepend( $('',{ class: 'fa fa-line-chart fa-fw' @@ -209,8 +208,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Effect info').prepend( $('',{ class: 'fa fa-crosshairs fa-fw' @@ -220,8 +218,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Jump info').prepend( $('',{ class: 'fa fa-space-shuttle fa-fw' @@ -233,8 +230,7 @@ define([ getMenuHeadline('Settings') ).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Account').prepend( $('',{ class: 'fa fa-user fa-fw' @@ -245,8 +241,7 @@ define([ ).append( $('', { class: 'list-group-item hide', // trigger by js - id: Util.config.menuButtonFullScreenId, - href: '#' + id: Util.config.menuButtonFullScreenId }).html('  Full screen').prepend( $('',{ class: 'glyphicon glyphicon-fullscreen', @@ -265,8 +260,7 @@ define([ }) ).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Notification test').prepend( $('',{ class: 'fa fa-volume-up fa-fw' @@ -278,8 +272,7 @@ define([ getMenuHeadline('Danger zone') ).append( $('', { - class: 'list-group-item list-group-item-danger', - href: '#' + class: 'list-group-item list-group-item-danger' }).html('  Delete account').prepend( $('',{ class: 'fa fa-user-times fa-fw' @@ -289,8 +282,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-warning', - href: '#' + class: 'list-group-item list-group-item-warning' }).html('  Logout').prepend( $('',{ class: 'fa fa-sign-in fa-fw' @@ -317,8 +309,7 @@ define([ class: 'list-group' }).append( $('', { - class: 'list-group-item', - href: '#' + class: 'list-group-item' }).html('  Information').prepend( $('',{ class: 'fa fa-street-view fa-fw' @@ -327,12 +318,11 @@ define([ $(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'}); }) ).append( - getMenuHeadline('Settings') + getMenuHeadline('Configuration') ).append( $('', { - class: 'list-group-item', - href: '#' - }).html('  Configuration').prepend( + class: 'list-group-item' + }).html('  Settings').prepend( $('',{ class: 'fa fa-gears fa-fw' }) @@ -342,8 +332,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonGridId, - href: '#' + id: Util.config.menuButtonGridId }).html('   Grid snapping').prepend( $('',{ class: 'glyphicon glyphicon-th' @@ -357,8 +346,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonMagnetizerId, - href: '#' + id: Util.config.menuButtonMagnetizerId }).html('   Magnetizing').prepend( $('',{ class: 'fa fa-magnet fa-fw' @@ -372,8 +360,7 @@ define([ ).append( $('', { class: 'list-group-item', - id: Util.config.menuButtonEndpointId, - href: '#' + id: Util.config.menuButtonEndpointId }).html('   Signatures').prepend( $('',{ class: 'fa fa-link fa-fw' @@ -388,8 +375,7 @@ define([ getMenuHeadline('Help') ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Manual').prepend( $('',{ class: 'fa fa-book fa-fw' @@ -399,8 +385,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Shortcuts').prepend( $('',{ class: 'fa fa-keyboard-o fa-fw' @@ -410,8 +395,7 @@ define([ }) ).append( $('', { - class: 'list-group-item list-group-item-info', - href: '#' + class: 'list-group-item list-group-item-info' }).html('  Task-Manager').prepend( $('',{ class: 'fa fa-tasks fa-fw' @@ -423,8 +407,7 @@ define([ getMenuHeadline('Danger zone') ).append( $('', { - class: 'list-group-item list-group-item-danger', - href: '#' + class: 'list-group-item list-group-item-danger' }).html('  Delete map').prepend( $('',{ class: 'fa fa-trash fa-fw' @@ -482,11 +465,13 @@ define([ }); // main menus - $('.' + config.headMenuClass).on('click', function() { + $('.' + config.headMenuClass).on('click', function(e) { + e.preventDefault(); slideMenu.slidebars.toggle('left'); }); - $('.' + config.headMapClass).on('click', function() { + $('.' + config.headMapClass).on('click', function(e) { + e.preventDefault(); slideMenu.slidebars.toggle('right'); }); @@ -872,7 +857,7 @@ define([ animateHeaderElement(userInfoElement, function(){ if(currentCharacterChanged){ userInfoElement.find('span').text( newCharacterName ); - userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' ); + userInfoElement.find('img').attr('src', Init.url.ccpImageServer + '/Character/' + newCharacterId + '_32.jpg' ); } // init "character switch" popover userInfoElement.initCharacterSwitchPopover(userData); @@ -894,7 +879,7 @@ define([ // toggle element animateHeaderElement(userShipElement, function(){ userShipElement.find('span').text( newShipName ); - userShipElement.find('img').attr('src', Init.url.ccpImageServer + 'Render/' + newShipId + '_32.png' ); + userShipElement.find('img').attr('src', Init.url.ccpImageServer + '/Render/' + newShipId + '_32.png' ); }, showShipElement); // set new id for next check @@ -1104,10 +1089,12 @@ define([ if( statusElement.data('status') !== status ){ // status has changed if(! programStatusInterval){ + // check if timer exists if not -> set default (in case of the "init" ajax call failed + let programStatusVisible = Init.timer ? Init.timer.PROGRAM_STATUS_VISIBLE : 5000; let timer = function(){ // change status on first timer iteration - if(programStatusCounter === Init.timer.PROGRAM_STATUS_VISIBLE){ + if(programStatusCounter === programStatusVisible){ statusElement.velocity('stop').velocity('fadeOut', { duration: Init.animationSpeed.headerLink, @@ -1134,8 +1121,8 @@ define([ } }; - if(! programStatusInterval){ - programStatusCounter = Init.timer.PROGRAM_STATUS_VISIBLE; + if(!programStatusInterval){ + programStatusCounter = programStatusVisible; programStatusInterval = setInterval(timer, 1000); } } diff --git a/public/js/v1.3.0/app/render.js b/public/js/v1.3.0/app/render.js new file mode 100644 index 00000000..bbe43ca5 --- /dev/null +++ b/public/js/v1.3.0/app/render.js @@ -0,0 +1,191 @@ +/** + * Render controller + */ + +define(['jquery', 'mustache'], function($, Mustache) { + + 'use strict'; + + /** + * init function will be called before and after a new module is loaded + * @param functionName + * @param config + */ + let initModule = function(functionName, config){ + + if( + typeof config.functions === 'object' && + typeof config.functions[functionName] === 'function' + ){ + config.functions[functionName](); + } + }; + + /** + * load a template and render is with Mustache + * @param config + * @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 + switch(config.link){ + case 'prepend': + config.position.prepend(content); + break; + case 'before': + config.position.before(content); + break; + case 'after': + config.position.after(content); + break; + default: + config.position.append(content); + } + } + + // init module function after render + initModule('after', config); + + + }); + }; + + /** + * convert JSON object into HTML highlighted string + * @param obj + */ + let highlightJson = (obj) => { + let multiplyString = (num, str) => { + let sb = []; + for (let i = 0; i < num; i++) { + sb.push(str); + } + return sb.join(''); + }; + + let dateObj = new Date(); + let regexpObj = new RegExp(); + let tab = multiplyString(1, ' '); + let isCollapsible = true; + let quoteKeys = false; + let expImageClicked = '(() => {let container=this.parentNode.nextSibling; container.style.display=container.style.display===\'none\'?\'inline\':\'none\'})();'; + + let checkForArray = function (obj) { + return obj && + typeof obj === 'object' && + typeof obj.length === 'number' && + !(obj.propertyIsEnumerable('length')); + }; + + let getRow = function (indent, data, isPropertyContent) { + let tabs = ''; + for (let i = 0; i < indent && !isPropertyContent; i++) tabs += tab; + if (data !== null && data.length > 0 && data.charAt(data.length - 1) !== '\n') + data = data + '\n'; + return tabs + data; + }; + + let formatLiteral = function (literal, quote, comma, indent, isArray, style) { + if (typeof literal === 'string') + literal = literal.split('<').join('<').split('>').join('>'); + let str = '' + quote + literal + quote + comma + ''; + if (isArray) str = getRow(indent, str); + return str; + }; + + let formatFunction = function (indent, obj) { + let tabs = ''; + for (let i = 0; i < indent; i++) tabs += tab; + let funcStrArray = obj.toString().split('\n'); + let str = ''; + for (let i = 0; i < funcStrArray.length; i++) { + str += ((i === 0) ? '' : tabs) + funcStrArray[i] + '\n'; + } + return str; + }; + + + let highlight = (obj, indent, addComma, isArray, isPropertyContent) => { + let html = ''; + + let comma = (addComma) ? ', ' : ''; + let type = typeof obj; + let clpsHtml = ''; + if (checkForArray(obj)) { + if (obj.length === 0) { + html += getRow(indent, '[ ]' + comma, isPropertyContent); + } else { + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, '[' + clpsHtml, isPropertyContent); + for (let i = 0; i < obj.length; i++) { + html += highlight(obj[i], indent + 1, i < (obj.length - 1), true, false); + } + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, clpsHtml + ']' + comma); + } + } else if (type === 'object') { + if (obj === null) { + html += formatLiteral('null', '', comma, indent, isArray, 'pf-code-Null'); + } else if (obj.constructor === dateObj.constructor) { + html += formatLiteral('new Date(' + obj.getTime() + ') /*' + obj.toLocaleString() + '*/', '', comma, indent, isArray, 'Date'); + } else if (obj.constructor === regexpObj.constructor) { + html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp'); + } else { + let numProps = 0; + for (let prop in obj) numProps++; + if (numProps === 0) { + html += getRow(indent, '{ }' + comma, isPropertyContent); + } else { + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, '{' + clpsHtml, isPropertyContent); + let j = 0; + for (let prop in obj) { + if (obj.hasOwnProperty(prop)) { + let quote = quoteKeys ? '"' : ''; + html += getRow(indent + 1, '' + quote + prop + quote + ': ' + highlight(obj[prop], indent + 1, ++j < numProps, false, true)); + } + } + clpsHtml = isCollapsible ? '' : ''; + html += getRow(indent, clpsHtml + '}' + comma); + } + } + } else if (type === 'number') { + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Number'); + } else if (type === 'boolean') { + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Boolean'); + } else if (type === 'function') { + if (obj.constructor === regexpObj.constructor) { + html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp'); + } else { + obj = formatFunction(indent, obj); + html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Function'); + } + } else if (type === 'undefined') { + html += formatLiteral('undefined', '', comma, indent, isArray, 'pf-code-Null'); + } else { + html += formatLiteral(obj.toString().split('\\').join('\\\\').split('"').join('\\"'), '"', comma, indent, isArray, 'pf-code-String'); + } + + return html; + }; + + return highlight(obj, 0, false, false, false); + }; + + return { + showModule: showModule, + highlightJson: highlightJson + }; +}); \ No newline at end of file diff --git a/public/js/v1.2.5/app/setup.js b/public/js/v1.3.0/app/setup.js similarity index 100% rename from public/js/v1.2.5/app/setup.js rename to public/js/v1.3.0/app/setup.js diff --git a/public/js/v1.2.5/app/ui/demo_map.js b/public/js/v1.3.0/app/ui/demo_map.js similarity index 100% rename from public/js/v1.2.5/app/ui/demo_map.js rename to public/js/v1.3.0/app/ui/demo_map.js diff --git a/public/js/v1.2.5/app/ui/dialog/account_settings.js b/public/js/v1.3.0/app/ui/dialog/account_settings.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/account_settings.js rename to public/js/v1.3.0/app/ui/dialog/account_settings.js diff --git a/public/js/v1.2.5/app/ui/dialog/credit.js b/public/js/v1.3.0/app/ui/dialog/credit.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/credit.js rename to public/js/v1.3.0/app/ui/dialog/credit.js diff --git a/public/js/v1.2.5/app/ui/dialog/delete_account.js b/public/js/v1.3.0/app/ui/dialog/delete_account.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/delete_account.js rename to public/js/v1.3.0/app/ui/dialog/delete_account.js diff --git a/public/js/v1.2.5/app/ui/dialog/jump_info.js b/public/js/v1.3.0/app/ui/dialog/jump_info.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/jump_info.js rename to public/js/v1.3.0/app/ui/dialog/jump_info.js diff --git a/public/js/v1.2.5/app/ui/dialog/manual.js b/public/js/v1.3.0/app/ui/dialog/manual.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/manual.js rename to public/js/v1.3.0/app/ui/dialog/manual.js diff --git a/public/js/v1.2.5/app/ui/dialog/map_info.js b/public/js/v1.3.0/app/ui/dialog/map_info.js similarity index 61% rename from public/js/v1.2.5/app/ui/dialog/map_info.js rename to public/js/v1.3.0/app/ui/dialog/map_info.js index 112ace43..fae0f9fb 100644 --- a/public/js/v1.2.5/app/ui/dialog/map_info.js +++ b/public/js/v1.3.0/app/ui/dialog/map_info.js @@ -6,9 +6,10 @@ define([ 'jquery', 'app/init', 'app/util', + 'app/render', 'bootbox', 'app/map/util' -], function($, Init, Util, bootbox, MapUtil) { +], function($, Init, Util, Render, bootbox, MapUtil) { 'use strict'; @@ -19,23 +20,27 @@ define([ // map info dialog/tabs dialogMapInfoSummaryId: 'pf-map-info-dialog-summary', // id for map "summary" container dialogMapInfoUsersId: 'pf-map-info-dialog-users', // id for map "user" container + dialogMapInfoLogsId: 'pf-map-info-dialog-logs', // id for map "logs" container dialogMapInfoRefreshId: 'pf-map-info-dialog-refresh', // id for map "refresh" container - // "summary" container + // dialog containers mapInfoId: 'pf-map-info', // id for map info mapInfoSystemsId: 'pf-map-info-systems', // id for map info systems box mapInfoConnectionsId: 'pf-map-info-connections', // id for map info connections box mapInfoUsersId: 'pf-map-info-users', // id for map info users box + mapInfoLogsId: 'pf-map-info-logs', // id for map info logs box - mapInfoTableClass: 'pf-map-info-table', // class for data mapInfoLifetimeCounterClass: 'pf-map-info-lifetime-counter', // class for map lifetime counter // dataTable - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells - tableImageSmallCellClass: 'pf-table-image-small-cell', // class for table "small image" cells - tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells - tableCounterCellClass: 'pf-table-counter-cell', // class for table "counter" cells - tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) + tableToolsClass: 'pf-table-tools', // class for table "tools" section (e.g. Buttons) + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells + tableCellImageSmallClass: 'pf-table-image-small-cell', // class for table "small image" cells + tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells + tableCellLinkClass: 'pf-table-link-cell', // class for table "links" cells + tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells + tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells + tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content) loadingOptions: { // config for loading overlay icon: { @@ -56,16 +61,26 @@ define([ btnOkIcon: 'fa fa-fw fa-close' }; + /** + * get image that marks a table cell as clickable + * @returns {string} + */ + let getIconForInformationWindow = () => { + return ''; + }; + /** * loads the map info data into an element * @param mapData */ - $.fn.loadMapInfoData = function(mapData){ - let mapElement = $(this); + $.fn.initMapInfoData = function(mapData){ + let mapElement = $(this).empty(); - mapElement.empty(); mapElement.showLoadingAnimation(config.loadingOptions); + // get some more config values from this map. Which are not part of "mapData" + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + let countSystems = mapData.data.systems.length; let countConnections = mapData.data.connections.length; @@ -80,11 +95,11 @@ define([ } } - // check max map limits (e.g. max systems per map) ============================================================ + // check max map limits (e.g. max systems per map) ------------------------------------------------------------ let percentageSystems = (100 / mapType.defaultConfig.max_systems) * countSystems; let maxSystemsClass = (percentageSystems < 90) ? 'txt-color-success' : (percentageSystems < 100) ? 'txt-color-warning' : 'txt-color-danger'; - // build content ============================================================================================== + // build content ---------------------------------------------------------------------------------------------- let dlElementLeft = $('
', { class: 'dl-horizontal', @@ -115,13 +130,6 @@ define([ class: 'dl-horizontal', css: {'float': 'right'} }).append( - $('
').text( 'Lifetime' ) - ).append( - $('
', { - class: config.mapInfoLifetimeCounterClass, - text: mapData.config.created - }) - ).append( $('
').text( 'Systems' ) ).append( $('
', { @@ -131,6 +139,17 @@ define([ $('
').text( 'Connections' ) ).append( $('
').text( countConnections ) + ).append( + $('
').text( 'Lifetime' ) + ).append( + $('
', { + class: config.mapInfoLifetimeCounterClass, + text: mapData.config.created + }) + ).append( + $('
').text( 'Created' ) + ).append( + $('
').text( mapDataOrigin.config.created.character.name) ); mapElement.append(dlElementRight); @@ -145,14 +164,11 @@ define([ * loads system info table into an element * @param mapData */ - $.fn.loadSystemInfoTable = function(mapData){ - - let systemsElement = $(this); - - systemsElement.empty(); + $.fn.initSystemInfoTable = function(mapData){ + let systemsElement = $(this).empty(); let systemTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); systemsElement.append(systemTable); @@ -292,7 +308,7 @@ define([ systemsData.push(tempData); } - let systemsDataTable = systemTable.dataTable( { + let systemsDataTable = systemTable.DataTable( { pageLength: 20, paging: true, lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']], @@ -343,7 +359,14 @@ define([ } },{ title: 'system', - data: 'name' + data: 'name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.id }); + }); + } },{ title: 'alias', data: 'alias' @@ -401,7 +424,7 @@ define([ title: 'updated', width: '80px', searchable: false, - className: ['text-right', config.tableCounterCellClass, 'min-desktop'].join(' '), + className: ['text-right', config.tableCellCounterClass, 'min-desktop'].join(' '), data: 'updated', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter(); @@ -418,7 +441,7 @@ define([ orderable: false, searchable: false, width: '10px', - className: ['text-center', config.tableActionCellClass].join(' '), + className: ['text-center', config.tableCellActionClass].join(' '), data: 'clear', createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { let tempTableElement = this; @@ -444,11 +467,11 @@ define([ Util.showNotify({title: 'System deleted', text: rowData.name, type: 'success'}); - // refresh connection table (connections might have changed) ================== + // refresh connection table (connections might have changed) -------------- let connectionsElement = $('#' + config.mapInfoConnectionsId); let mapDataNew = activeMap.getMapDataFromClient({forceData: true}); - connectionsElement.loadConnectionInfoTable(mapDataNew); + connectionsElement.initConnectionInfoTable(mapDataNew); }else{ // error Util.showNotify({title: 'Failed to delete system', text: rowData.name, type: 'error'}); @@ -472,13 +495,11 @@ define([ * loads connection info table into an element * @param mapData */ - $.fn.loadConnectionInfoTable = function(mapData){ - let connectionsElement = $(this); - - connectionsElement.empty(); + $.fn.initConnectionInfoTable = function(mapData){ + let connectionsElement = $(this).empty(); let connectionTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); connectionsElement.append(connectionTable); @@ -489,7 +510,7 @@ define([ connectionsElement.hideLoadingAnimation(); }); - // connections table ========================================================================================== + // connections table ------------------------------------------------------------------------------------------ // prepare data for dataTables let connectionData = []; @@ -505,8 +526,10 @@ define([ scope_sort: tempConnectionData.scope }; - // source system name - tempConData.source = tempConnectionData.sourceName; + tempConData.source = { + id: tempConnectionData.source, + name: tempConnectionData.sourceName, + }; // connection let connectionClasses = []; @@ -519,8 +542,10 @@ define([ tempConData.connection = '
'; - - tempConData.target = tempConnectionData.targetName; + tempConData.target = { + id: tempConnectionData.target, + name: tempConnectionData.targetName, + }; tempConData.updated = tempConnectionData.updated; @@ -557,7 +582,14 @@ define([ } },{ title: 'source system', - data: 'source' + data: 'source.name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.source.id }); + }); + } },{ title: 'connection', width: '80px', @@ -567,12 +599,19 @@ define([ data: 'connection' }, { title: 'target system', - data: 'target' + data: 'target.name', + className: [config.tableCellLinkClass].join(' '), + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // select system + $(cell).on('click', function(e){ + Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.target.id }); + }); + } },{ title: 'updated', width: '80px', searchable: false, - className: ['text-right', config.tableCounterCellClass].join(' '), + className: ['text-right', config.tableCellCounterClass].join(' '), data: 'updated', createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter(); @@ -589,7 +628,7 @@ define([ orderable: false, searchable: false, width: '10px', - className: ['text-center', config.tableActionCellClass].join(' '), + className: ['text-center', config.tableCellActionClass].join(' '), data: 'clear', createdCell: function(cell, cellData, rowData, rowIndex, colIndex) { let tempTableElement = this; @@ -621,13 +660,11 @@ define([ * loads user info table into an element * @param mapData */ - $.fn.loadUsersInfoTable = function(mapData){ - let usersElement = $(this); - - usersElement.empty(); + $.fn.initUsersInfoTable = function(mapData){ + let usersElement = $(this).empty(); let userTable = $('
', { - class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ') + class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); usersElement.append(userTable); @@ -644,11 +681,7 @@ define([ }); }); - let getIconForInformationWindow = () => { - return ''; - }; - - // users table ================================================================================================ + // users table ------------------------------------------------------------------------------------------------ // prepare users data for dataTables let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id ); let usersData = []; @@ -688,13 +721,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '), + className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '), data: 'log.ship', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -721,13 +754,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: [config.tableImageCellClass].join(' '), + className: [config.tableCellImageClass].join(' '), data: 'id', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -737,7 +770,7 @@ define([ title: 'pilot', orderable: true, searchable: true, - className: [config.tableActionCellClass].join(' '), + className: [config.tableCellActionClass].join(' '), data: 'name', render: { _: function(data, type, row, meta){ @@ -761,13 +794,13 @@ define([ width: '26px', orderable: false, searchable: false, - className: [config.tableImageCellClass, config.tableImageSmallCellClass].join(' '), + className: [config.tableCellImageClass, config.tableCellImageSmallClass].join(' '), data: 'corporation', render: { _: function(data, type, row, meta){ let value = data; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -777,7 +810,7 @@ define([ title: 'corporation', orderable: true, searchable: true, - className: [config.tableActionCellClass].join(' '), + className: [config.tableCellActionClass].join(' '), data: 'corporation', render: { _: function (data, type, row, meta) { @@ -821,6 +854,316 @@ define([ }; + /** + * loads logs table into an element + * @param mapData + */ + $.fn.initLogsInfoTable = function(mapData){ + let logsElement = $(this).empty(); + + /** + * ajax load function for log fdata + * @param requestData + * @param context + */ + let getLogsData = (requestData, context) => { + context.logsElement.showLoadingAnimation(config.loadingOptions); + + $.ajax({ + type: 'POST', + url: Init.path.getMapLogData, + data: requestData, + dataType: 'json', + context: context + }).done(function(data){ + this.callback(data, context); + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': loadLogs', text: reason, type: 'warning'}); + }).always(function(){ + this.logsElement.hideLoadingAnimation(); + }); + }; + + /** + * callback function after ajax response with log data + * @param responseData + * @param context + */ + let updateTableDataCallback = (responseData, context) => { + let newLogCount = responseData.data.length; + + if(newLogCount > 0){ + let pageInfoOld = context.tableApi.page.info(); + + // add new rows + context.tableApi.rows.add(responseData.data).draw(); + + let newPageIndex = 0; + if(pageInfoOld.recordsDisplay === 0){ + Util.showNotify({title: 'New logs loaded', text: newLogCount + ' most recent logs added', type: 'success'}); + }else{ + // get new pageInfo (new max page count) + let pageInfoNew = context.tableApi.page.info(); + newPageIndex = Math.max(0, pageInfoNew.pages - 1); + Util.showNotify({title: 'More logs loaded', text: newLogCount + ' older logs added', type: 'info'}); + } + + // get to last page (pageIndex starts at zero) -> check if last page > 0 + context.tableApi.page(newPageIndex).draw(false); + }else{ + Util.showNotify({title: 'No logs found', text: 'No more entries', type: 'danger'}); + } + + }; + + // init logs table -------------------------------------------------------------------------------------------- + let logTable = $('
', { + class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed'].join(' ') + }); + logsElement.append(logTable); + + let serverDate = Util.getServerTime(); + let serverHours = serverDate.setHours(0,0,0,0); + + let logDataTable = logTable.DataTable({ + pageLength: 25, + paging: true, + lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]], + pagingType: 'full_numbers', + ordering: false, + autoWidth: false, + searching: true, + hover: false, + data: [], + language: { + emptyTable: 'No logs available', + zeroRecords: 'No logs found', + lengthMenu: 'Show _MENU_ rows', + info: 'Showing _START_ to _END_ of _TOTAL_ rows' + }, + columnDefs: [ + { + targets: 0, + title: ' ', + width: 12, + data: 'context.tag', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + let className = 'txt-color-' + data; + value = ''; + } + return value; + } + } + },{ + targets: 1, + name: 'timestamp', + title: '', + width: 100, + className: ['text-right'].join(' '), + data: 'datetime.date', + render: { + _: function(data, type, row, meta){ + // strip microseconds + let logDateString = data.substring(0, 19) ; + let logDate = new Date(logDateString.replace(/-/g, '/')); + data = Util.convertDateToString(logDate, true); + + // check whether log is new (today) -> + if(logDate.setHours(0,0,0,0) === serverHours) { + // replace dd/mm/YYYY + data = 'today' + data.substring(10); + } + return data; + } + } + },{ + targets: 2, + title: 'level', + width: 40, + data: 'level_name' + },{ + targets: 3, + title: 'channel', + className: [config.tableCellEllipsisClass].join(' '), + width: 40, + data: 'channel' + },{ + targets: 4, + title: 'message', + width: 115, + data: 'message', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + let className = 'txt-color-'; + if(Util.getObjVal(row, 'context.tag')){ + className += row.context.tag; + } + value = '' + value + ''; + } + return value; + } + } + },{ + targets: 5, + title: '', + width: 26, + searchable: false, + className: [config.tableCellImageClass].join(' '), + data: 'context.data.character.id' , + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value = ''; + } + return value; + } + } + },{ + targets: 6, + title: 'pilot', + width: 110, + className: [config.tableCellActionClass].join(' '), + data: 'context.data.character.name', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + value += ' ' + getIconForInformationWindow(); + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // open character information window (ingame) + $(cell).on('click', { tableApi: this.DataTable() }, function(e) { + let rowData = e.data.tableApi.row(this).data(); + Util.openIngameWindow(rowData.context.data.character.id); + }); + } + },{ + targets: 7, + title: 'context', + className: [config.tableCellEllipsisClass].join(' '), + data: 'context.data.formatted' + },{ + targets: 8, + title: '', + width: 12, + className: [config.tableCellActionClass].join(' '), + data: 'context.data', + render: { + _: function(data, type, row, meta){ + let value = data; + if(type === 'display'){ + // txt-color-redDarker + value = ''; + } + return value; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + // unset formatted string (to much content) + + if(cellData.formatted){ + // clone data before delete() values + cellData = Object.assign({}, cellData); + delete(cellData.formatted); + } + + let jsonHighlighted = Render.highlightJson(cellData); + let content = '
' + jsonHighlighted + '
'; + + // open popover with raw log data + $(cell).popover({ + placement: 'left', + html: true, + trigger: 'hover', + content: content, + container: 'body', + title: 'Raw data', + delay: { + show: 180, + hide: 0 + } + }); + } + } + ], + initComplete: function(settings){ + let tableApi = this.api(); + + // empty table is ready -> load logs + getLogsData({ + mapId: mapData.config.id + }, { + tableApi: tableApi, + callback: updateTableDataCallback, + logsElement: logsElement + }); + }, + drawCallback: function(settings){ + let tableApi = this.api(); + + // en/disable "load more" button ---------------------------------------------------------------------- + let tableInfo = tableApi.page.info(); + let isLastPage = (tableInfo.pages === 0 || tableInfo.page === tableInfo.pages - 1); + tableApi.button(0).enable(isLastPage); + + // adjust "timestamp" column width -------------------------------------------------------------------- + let timestampColumn = tableApi.column('timestamp:name').header(); + let timestampColumnCells = tableApi.cells(undefined, 'timestamp:name', {page: 'current', order:'current'}); + + let hasOldLogs = timestampColumnCells.render( 'display' ).reduce((hasOldLogs, cellValue) => { + return (hasOldLogs === false && !cellValue.startsWith('today')) ? true : hasOldLogs; + }, false); + + if(hasOldLogs){ + $(timestampColumn).css({width: '100px'}); + }else{ + $(timestampColumn).css({width: '80px'}); + } + } + }); + + // ------------------------------------------------------------------------------------------------------------ + // add dataTable buttons (extension) + logsElement.append($('
', { + class: config.tableToolsClass + })); + + let buttons = new $.fn.dataTable.Buttons( logDataTable, { + buttons: [ + { + className: 'btn btn-sm btn-default', + text: ' load more', + enabled: false, + action: function ( e, dt, node, config ) { + let pageInfo = dt.page.info(); + + getLogsData({ + mapId: mapData.config.id, + limit: pageInfo.length, + offset: pageInfo.recordsTotal + }, { + tableApi: dt, + callback: updateTableDataCallback, + logsElement: logsElement + }); + } + } + ] + } ); + + logDataTable.buttons().container().appendTo( $(this).find('.' + config.tableToolsClass)); + }; + /** * shows the map information modal dialog * @param options @@ -830,21 +1173,29 @@ define([ let mapData = activeMap.getMapDataFromClient({forceData: true}); if(mapData !== false){ - requirejs(['text!templates/dialog/map_info.html', 'mustache'], function(template, Mustache) { + // "log" tab -> get "Origin", not all config options are set in mapData + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + + requirejs(['text!templates/dialog/map_info.html', 'mustache', 'datatables.loader'], (template, Mustache) => { let data = { dialogSummaryContainerId: config.dialogMapInfoSummaryId, dialogUsersContainerId: config.dialogMapInfoUsersId, + dialogLogsContainerId: config.dialogMapInfoLogsId, dialogRefreshContainerId: config.dialogMapInfoRefreshId, dialogNavigationClass: config.dialogNavigationClass, mapInfoId: config.mapInfoId, mapInfoSystemsId: config.mapInfoSystemsId, mapInfoConnectionsId: config.mapInfoConnectionsId, mapInfoUsersId: config.mapInfoUsersId, + mapInfoLogsId: config.mapInfoLogsId, + + logHistoryEnabled: Boolean(Util.getObjVal(mapDataOrigin, 'config.logging.history')), // default open tab ---------- openTabInformation: options.tab === 'information', - openTabActivity: options.tab === 'activity' + openTabActivity: options.tab === 'activity', + openTabLog: options.tab === 'log' }; let content = Mustache.render(template, data); @@ -865,39 +1216,51 @@ define([ }); mapInfoDialog.on('shown.bs.modal', function(e) { - // modal on open - let mapElement = $('#' + config.mapInfoId); let systemsElement = $('#' + config.mapInfoSystemsId); let connectionsElement = $('#' + config.mapInfoConnectionsId); let usersElement = $('#' + config.mapInfoUsersId); - // set refresh button observer - $('#' + config.dialogMapInfoRefreshId).on('click', function(){ + $('#' + config.dialogMapInfoRefreshId).on('click', function(e){ let menuAction = $(this).attr('data-action'); if(menuAction === 'refresh'){ // get new map data let mapData = activeMap.getMapDataFromClient({forceData: true}); + // find active tab + let activeTabLink = $(this).parents('.navbar').find('.navbar-header.pull-left li.active a'); + if(activeTabLink.attr('href') === '#' + config.dialogMapInfoLogsId){ + $('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin); + } - mapElement.loadMapInfoData(mapData); - systemsElement.loadSystemInfoTable(mapData); - connectionsElement.loadConnectionInfoTable(mapData); - usersElement.loadUsersInfoTable(mapData); + mapElement.initMapInfoData(mapData); + systemsElement.initSystemInfoTable(mapData); + connectionsElement.initConnectionInfoTable(mapData); + usersElement.initUsersInfoTable(mapData); } }); // load map data - mapElement.loadMapInfoData(mapData); + mapElement.initMapInfoData(mapData); // load system table - systemsElement.loadSystemInfoTable(mapData); + systemsElement.initSystemInfoTable(mapData); // load connection table - connectionsElement.loadConnectionInfoTable(mapData); + connectionsElement.initConnectionInfoTable(mapData); // load users table - usersElement.loadUsersInfoTable(mapData); + usersElement.initUsersInfoTable(mapData); + + }); + + // events for tab change + mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){ + if($(e.target).attr('href') === '#' + config.dialogMapInfoLogsId){ + // "log" tab + let mapDataOrigin = Util.getCurrentMapData(mapData.config.id); + $('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin); + } }); }); diff --git a/public/js/v1.2.5/app/ui/dialog/map_settings.js b/public/js/v1.3.0/app/ui/dialog/map_settings.js similarity index 79% rename from public/js/v1.2.5/app/ui/dialog/map_settings.js rename to public/js/v1.3.0/app/ui/dialog/map_settings.js index ecba3f2f..6a4a2dff 100644 --- a/public/js/v1.2.5/app/ui/dialog/map_settings.js +++ b/public/js/v1.3.0/app/ui/dialog/map_settings.js @@ -24,6 +24,15 @@ define([ deleteEolConnectionsId: 'pf-map-dialog-delete-connections-eol', // id for "deleteEOLConnections" checkbox persistentAliasesId: 'pf-map-dialog-persistent-aliases', // id for "persistentAliases" checkbox + logHistoryId: 'pf-map-dialog-history', // id for "history logging" checkbox + logActivityId: 'pf-map-dialog-activity', // id for "activity" checkbox + + slackWebHookURLId: 'pf-map-dialog-slack-url', // id for Slack "webHookUrl" + slackUsernameId: 'pf-map-dialog-slack-username', // id for Slack "username" + slackIconId: 'pf-map-dialog-slack-icon', // id for Slack "icon" + slackChannelHistoryId: 'pf-map-dialog-slack-channel-history', // id for Slack channel "history" + slackChannelRallyId: 'pf-map-dialog-slack-channel-rally', // id for Slack channel "rally" + characterSelectId: 'pf-map-dialog-character-select', // id for "character" select corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select @@ -67,7 +76,7 @@ define([ requirejs([ 'text!templates/dialog/map.html', - 'text!templates/form/map_settings.html', + 'text!templates/form/map.html', 'mustache' ], function(templateMapDialog, templateMapSettings, Mustache) { @@ -96,10 +105,10 @@ define([ formInfoContainerClass: Util.config.formInfoContainerClass }; - // render "new map" tab content ------------------------------------------- + // render "new map" tab content ----------------------------------------------------------------------- let contentNewMap = Mustache.render(templateMapSettings, data); - // render "edit map" tab content ------------------------------------------ + // render "edit map" tab content ---------------------------------------------------------------------- let contentEditMap = Mustache.render(templateMapSettings, data); contentEditMap = $(contentEditMap); @@ -111,6 +120,19 @@ define([ let deleteEolConnections = true; let persistentAliases = true; + let logActivity = true; + let logHistory = true; + + let slackWebHookURL = ''; + let slackUsername = ''; + let slackIcon = ''; + let slackChannelHistory = ''; + let slackChannelRally = ''; + let slackEnabled = false; + let slackHistoryEnabled = false; + let slackRallyEnabled = false; + let slackSectionShow = false; + if(mapData !== false){ // set current map information contentEditMap.find('input[name="id"]').val( mapData.config.id ); @@ -126,9 +148,26 @@ define([ deleteExpiredConnections = mapData.config.deleteExpiredConnections; deleteEolConnections = mapData.config.deleteEolConnections; persistentAliases = mapData.config.persistentAliases; + + logActivity = mapData.config.logging.activity; + logHistory = mapData.config.logging.history; + + slackWebHookURL = mapData.config.logging.slackWebHookURL; + slackUsername = mapData.config.logging.slackUsername; + slackIcon = mapData.config.logging.slackIcon; + slackChannelHistory = mapData.config.logging.slackChannelHistory; + slackChannelRally = mapData.config.logging.slackChannelRally; + slackEnabled = Boolean(Util.getObjVal(Init, 'slack.status')); + slackHistoryEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled')); + slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled')); + slackSectionShow = (slackEnabled && slackWebHookURL.length > 0); + + // remove "#" from Slack channels + slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory; + slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally; } - // render main dialog ----------------------------------------------------- + // render main dialog --------------------------------------------------------------------------------- data = { id: config.newMapDialogId, mapData: mapData, @@ -162,6 +201,26 @@ define([ deleteEolConnections: deleteEolConnections, persistentAliases: persistentAliases, + logHistoryId: config.logHistoryId, + logActivityId: config.logActivityId, + logActivity: logActivity, + logHistory: logHistory, + + slackWebHookURLId: config.slackWebHookURLId, + slackUsernameId: config.slackUsernameId, + slackIconId: config.slackIconId, + slackChannelHistoryId: config.slackChannelHistoryId, + slackChannelRallyId: config.slackChannelRallyId, + slackWebHookURL: slackWebHookURL, + slackUsername: slackUsername, + slackIcon: slackIcon, + slackChannelHistory: slackChannelHistory, + slackChannelRally: slackChannelRally, + slackEnabled: slackEnabled, + slackHistoryEnabled: slackHistoryEnabled, + slackRallyEnabled: slackRallyEnabled, + slackSectionShow: slackSectionShow, + characterSelectId: config.characterSelectId, corporationSelectId: config.corporationSelectId, allianceSelectId: config.allianceSelectId, @@ -244,6 +303,15 @@ define([ // get form data let formData = form.getFormValues(); + // add value prefixes (Slack channels) + let tmpVal; + if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelHistory')) === 'string' && tmpVal.length){ + formData.slackChannelHistory = '#' + tmpVal; + } + if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelRally')) === 'string' && tmpVal.length){ + formData.slackChannelRally = '#' + tmpVal; + } + // checkbox fix -> settings tab if( form.find('#' + config.deleteExpiredConnectionsId).length ){ formData.deleteExpiredConnections = formData.hasOwnProperty('deleteExpiredConnections') ? parseInt( formData.deleteExpiredConnections ) : 0; @@ -254,6 +322,15 @@ define([ if( form.find('#' + config.persistentAliasesId).length ){ formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0; } + if( form.find('#' + config.persistentAliasesId).length ){ + formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0; + } + if( form.find('#' + config.logHistoryId).length ){ + formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0; + } + if( form.find('#' + config.logActivityId).length ){ + formData.logActivity = formData.hasOwnProperty('logActivity') ? parseInt( formData.logActivity ) : 0; + } let requestData = {formData: formData}; @@ -264,8 +341,6 @@ define([ dataType: 'json' }).done(function(responseData){ - dialogContent.hideLoadingAnimation(); - if(responseData.error.length){ form.showFormMessage(responseData.error); }else{ @@ -287,6 +362,8 @@ define([ Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); + }).always(function() { + dialogContent.hideLoadingAnimation(); }); } @@ -297,10 +374,13 @@ define([ }); - // after modal is shown ======================================================================= + // after modal is shown =============================================================================== mapInfoDialog.on('shown.bs.modal', function(e){ mapInfoDialog.initTooltips(); + // manually trigger the "show" event for the initial active tab (not triggered by default...) + mapInfoDialog.find('.navbar li.active a[data-toggle=tab]').trigger('shown.bs.tab'); + // prevent "disabled" tabs from being clicked... "bootstrap" bugFix... mapInfoDialog.find('.navbar a[data-toggle=tab]').on('click', function(e){ if ($(this).hasClass('disabled')){ @@ -323,17 +403,12 @@ define([ form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]); } - // init select fields in case "settings" tab is open by default - if(options.tab === 'settings'){ - initSettingsSelectFields(mapInfoDialog); - } - - // init "download tab" ======================================================================== + // init "download tab" ============================================================================ let downloadTabElement = mapInfoDialog.find('#' + config.dialogMapDownloadContainerId); if(downloadTabElement.length){ // tab exists - // export map data ------------------------------------------------------------------------ + // export map data ---------------------------------------------------------------------------- downloadTabElement.find('#' + config.buttonExportId).on('click', { mapData: mapData }, function(e){ let exportForm = $('#' + config.dialogMapExportFormId); @@ -364,7 +439,7 @@ define([ } }); - // import map data ------------------------------------------------------------------------ + // import map data ---------------------------------------------------------------------------- // check if "FileReader" API is supported let importFormElement = downloadTabElement.find('#' + config.dialogMapImportFormId); if(window.File && window.FileReader && window.FileList && window.Blob){ @@ -477,16 +552,22 @@ define([ } }); - // events for tab change + // events for tab change ------------------------------------------------------------------------------ mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){ + let modalDialog = mapInfoDialog.find('div.modal-dialog'); let selectElementCharacter = mapInfoDialog.find('#' + config.characterSelectId); let selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId); let selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId); + if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){ - // "settings" tab + // "settings" tab -> resize modal + modalDialog.toggleClass('modal-lg', true); initSettingsSelectFields(mapInfoDialog); }else{ + // resize modal + modalDialog.toggleClass('modal-lg', false); + if( $(selectElementCharacter).data('select2') !== undefined ){ $(selectElementCharacter).select2('destroy'); } @@ -611,29 +692,38 @@ define([ * @param mapData */ $.fn.showDeleteMapDialog = function(mapData){ - let mapName = mapData.config.name; + let mapNameStr = '' + mapName + ''; - let mapDeleteDialog = bootbox.confirm('Delete map "' + mapName + '"?', function(result){ - if(result){ - let data = {mapData: mapData.config}; + let mapDeleteDialog = bootbox.confirm({ + message: 'Delete map "' + mapNameStr + '"?', + buttons: { + confirm: { + label: ' delete map', + className: 'btn-danger' + } + }, + callback: function(result){ + if(result){ + let data = {mapData: mapData.config}; - $.ajax({ - type: 'POST', - url: Init.path.deleteMap, - data: data, - dataType: 'json' - }).done(function(data){ - Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'}); - }).fail(function( jqXHR, status, error) { - let reason = status + ' ' + error; - Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'}); - $(document).setProgramStatus('problem'); - }).always(function() { - $(mapDeleteDialog).modal('hide'); - }); + $.ajax({ + type: 'POST', + url: Init.path.deleteMap, + data: data, + dataType: 'json' + }).done(function(data){ + Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'}); + }).fail(function( jqXHR, status, error) { + let reason = status + ' ' + error; + Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'}); + $(document).setProgramStatus('problem'); + }).always(function() { + $(mapDeleteDialog).modal('hide'); + }); - return false; + return false; + } } }); diff --git a/public/js/v1.2.5/app/ui/dialog/notification.js b/public/js/v1.3.0/app/ui/dialog/notification.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/notification.js rename to public/js/v1.3.0/app/ui/dialog/notification.js diff --git a/public/js/v1.2.5/app/ui/dialog/releases.js b/public/js/v1.3.0/app/ui/dialog/releases.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/releases.js rename to public/js/v1.3.0/app/ui/dialog/releases.js diff --git a/public/js/v1.2.5/app/ui/dialog/shortcuts.js b/public/js/v1.3.0/app/ui/dialog/shortcuts.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/shortcuts.js rename to public/js/v1.3.0/app/ui/dialog/shortcuts.js diff --git a/public/js/v1.2.5/app/ui/dialog/stats.js b/public/js/v1.3.0/app/ui/dialog/stats.js similarity index 80% rename from public/js/v1.2.5/app/ui/dialog/stats.js rename to public/js/v1.3.0/app/ui/dialog/stats.js index 0d797751..0b814f9d 100644 --- a/public/js/v1.2.5/app/ui/dialog/stats.js +++ b/public/js/v1.3.0/app/ui/dialog/stats.js @@ -8,7 +8,8 @@ define([ 'app/init', 'app/util', 'app/render', - 'bootbox' + 'bootbox', + 'peityInlineChart' ], function($, Init, Util, Render, bootbox) { 'use strict'; @@ -25,7 +26,7 @@ define([ // stats/dataTable statsContainerId: 'pf-stats-dialog-container', // class for statistics container (dynamic ajax content) statsTableId: 'pf-stats-table', // id for statistics table element - tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells + tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells // charts statsLineChartClass: 'pf-line-chart' // class for inline chart elements @@ -36,7 +37,9 @@ define([ * @param dialogElement */ let initStatsTable = function(dialogElement){ - let columnNumberWidth = 35; + let columnNumberWidth = 28; + let cellPadding = 4; + let lineChartWidth = columnNumberWidth + (2 * cellPadding); let lineColor = '#477372'; // render function for inline-chart columns @@ -71,7 +74,7 @@ define([ lengthMenu: [[10, 20, 30, 50], [10, 20, 30, 50]], paging: true, ordering: true, - order: [ 16, 'desc' ], + order: [ 20, 'desc' ], info: true, searching: true, hover: false, @@ -97,11 +100,11 @@ define([ orderable: false, searchable: false, width: 26, - className: ['text-center', config.tableImageCellClass].join(' '), + className: ['text-center', config.tableCellImageClass].join(' '), data: 'character', render: { _: function(data, type, row, meta){ - return ''; + return ''; } } },{ @@ -134,7 +137,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemCreate', + data: 'mapCreate', render: { _: renderInlineChartColumn } @@ -145,7 +148,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemUpdate', + data: 'mapUpdate', render: { _: renderInlineChartColumn } @@ -156,7 +159,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'systemDelete', + data: 'mapDelete', render: { _: renderInlineChartColumn } @@ -166,7 +169,7 @@ define([ searchable: false, width: 20, className: ['text-right', 'separator-right'].join(' ') , - data: 'systemSum', + data: 'mapSum', render: { _: renderNumericColumn } @@ -177,7 +180,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionCreate', + data: 'systemCreate', render: { _: renderInlineChartColumn } @@ -188,7 +191,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionUpdate', + data: 'systemUpdate', render: { _: renderInlineChartColumn } @@ -199,7 +202,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'connectionDelete', + data: 'systemDelete', render: { _: renderInlineChartColumn } @@ -208,8 +211,8 @@ define([ title: 'Σ  ', searchable: false, width: 20, - className: ['text-right', 'separator-right'].join(' '), - data: 'connectionSum', + className: ['text-right', 'separator-right'].join(' ') , + data: 'systemSum', render: { _: renderNumericColumn } @@ -220,7 +223,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureCreate', + data: 'connectionCreate', render: { _: renderInlineChartColumn } @@ -231,7 +234,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureUpdate', + data: 'connectionUpdate', render: { _: renderInlineChartColumn } @@ -242,7 +245,7 @@ define([ searchable: false, width: columnNumberWidth, className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), - data: 'signatureDelete', + data: 'connectionDelete', render: { _: renderInlineChartColumn } @@ -252,12 +255,55 @@ define([ searchable: false, width: 20, className: ['text-right', 'separator-right'].join(' '), - data: 'signatureSum', + data: 'connectionSum', render: { _: renderNumericColumn } },{ targets: 16, + title: 'C  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureCreate', + render: { + _: renderInlineChartColumn + } + },{ + targets: 17, + title: 'U  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureUpdate', + render: { + _: renderInlineChartColumn + } + },{ + targets: 18, + title: 'D  ', + orderable: false, + searchable: false, + width: columnNumberWidth, + className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '), + data: 'signatureDelete', + render: { + _: renderInlineChartColumn + } + },{ + targets: 19, + title: 'Σ  ', + searchable: false, + width: 20, + className: ['text-right', 'separator-right'].join(' '), + data: 'signatureSum', + render: { + _: renderNumericColumn + } + },{ + targets: 20, title: 'Σ  ', searchable: false, width: 20, @@ -277,17 +323,17 @@ define([ }, drawCallback: function(settings){ this.api().rows().nodes().to$().each(function(i, row){ - $(row).find('.' + config.statsLineChartClass).peity('line', { + $($(row).find('.' + config.statsLineChartClass)).peity('line', { fill: 'transparent', height: 18, min: 0, - width: 50 + width: lineChartWidth }); }); }, footerCallback: function ( row, data, start, end, display ) { let api = this.api(); - let sumColumnIndexes = [7, 11, 15, 16]; + let sumColumnIndexes = [7, 11, 15, 19, 20]; // column data for "sum" columns over this page let pageTotalColumns = api @@ -310,7 +356,16 @@ define([ statsTable.on('order.dt search.dt', function(){ statsTable.column(0, {search:'applied', order:'applied'}).nodes().each(function(cell, i){ - $(cell).html( (i + 1) + '.  '); + let rowCount = i + 1; + let content = ''; + switch(rowCount){ + case 1: content = ''; break; + case 2: content = ''; break; + case 3: content = ''; break; + default: content = rowCount + '.  '; + } + + $(cell).html(content); }); }).draw(); @@ -413,6 +468,9 @@ define([ let currentWeek = weekStart; let formattedWeeksData = { + mapCreate: [], + mapUpdate: [], + mapDelete: [], systemCreate: [], systemUpdate: [], systemDelete: [], @@ -422,6 +480,7 @@ define([ signatureCreate: [], signatureUpdate: [], signatureDelete: [], + mapSum: 0, systemSum: 0, connectionSum: 0, signatureSum: 0 @@ -433,6 +492,16 @@ define([ if(weeksData.hasOwnProperty( yearWeekProp )){ let weekData = weeksData[ yearWeekProp ]; + // map ---------------------------------------------------------------------------------- + formattedWeeksData.mapCreate.push( weekData.mapCreate ); + formattedWeeksData.mapSum += parseInt( weekData.mapCreate ); + + formattedWeeksData.mapUpdate.push( weekData.mapUpdate ); + formattedWeeksData.mapSum += parseInt( weekData.mapUpdate ); + + formattedWeeksData.mapDelete.push( weekData.mapDelete ); + formattedWeeksData.mapSum += parseInt( weekData.mapDelete ); + // system ------------------------------------------------------------------------------- formattedWeeksData.systemCreate.push( weekData.systemCreate ); formattedWeeksData.systemSum += parseInt( weekData.systemCreate ); @@ -463,6 +532,11 @@ define([ formattedWeeksData.signatureDelete.push( weekData.signatureDelete ); formattedWeeksData.signatureSum += parseInt( weekData.signatureDelete ); }else{ + // map ------------------------------------------------------------------------------- + formattedWeeksData.mapCreate.push(0); + formattedWeeksData.mapUpdate.push(0); + formattedWeeksData.mapDelete.push(0); + // system ------------------------------------------------------------------------------- formattedWeeksData.systemCreate.push(0); formattedWeeksData.systemUpdate.push(0); @@ -487,6 +561,11 @@ define([ } } + // map --------------------------------------------------------------------------------------- + formattedWeeksData.mapCreate = formattedWeeksData.mapCreate.join(','); + formattedWeeksData.mapUpdate = formattedWeeksData.mapUpdate.join(','); + formattedWeeksData.mapDelete = formattedWeeksData.mapDelete.join(','); + // system --------------------------------------------------------------------------------------- formattedWeeksData.systemCreate = formattedWeeksData.systemCreate.join(','); formattedWeeksData.systemUpdate = formattedWeeksData.systemUpdate.join(','); @@ -515,6 +594,19 @@ define([ name: data.name, lastLogin: data.lastLogin }, + mapCreate: { + type: 'C', + data: formattedWeeksData.mapCreate + }, + mapUpdate: { + type: 'U', + data: formattedWeeksData.mapUpdate + }, + mapDelete: { + type: 'D', + data: formattedWeeksData.mapDelete + }, + mapSum: formattedWeeksData.mapSum, systemCreate: { type: 'C', data: formattedWeeksData.systemCreate @@ -554,7 +646,8 @@ define([ data: formattedWeeksData.signatureDelete }, signatureSum: formattedWeeksData.signatureSum, - totalSum: formattedWeeksData.systemSum + formattedWeeksData.connectionSum + formattedWeeksData.signatureSum + totalSum: formattedWeeksData.mapSum + formattedWeeksData.systemSum + + formattedWeeksData.connectionSum + formattedWeeksData.signatureSum }; formattedData.push(rowData); @@ -602,18 +695,18 @@ define([ * @param type * @returns {boolean} */ - let isTabTypeEnabled = function(type){ + let isTabTypeEnabled = (type) => { let enabled = false; switch(type){ case 'private': - if(Init.mapTypes.private.defaultConfig.activity_logging){ + if( Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) ){ enabled = true; } break; case 'corporation': if( - Init.mapTypes.corporation.defaultConfig.activity_logging && + Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) && Util.getCurrentUserInfo('corporationId') ){ enabled = true; @@ -621,7 +714,7 @@ define([ break; case 'alliance': if( - Init.mapTypes.alliance.defaultConfig.activity_logging && + Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) && Util.getCurrentUserInfo('allianceId') ){ enabled = true; @@ -636,15 +729,46 @@ define([ * show activity stats dialog */ $.fn.showStatsDialog = function(){ - requirejs(['text!templates/dialog/stats.html', 'mustache', 'peityInlineChart'], function(template, Mustache) { + requirejs(['text!templates/dialog/stats.html', 'mustache', 'datatables.loader'], function(template, Mustache) { + // get current statistics map settings + let logActivityEnabled = false; + let activeMap = Util.getMapModule().getActiveMap(); + if(activeMap){ + let activeMapId = activeMap.data('id'); + let activeMapData = Util.getCurrentMapData(activeMapId); + if(activeMapData){ + logActivityEnabled = Boolean(Util.getObjVal(activeMapData, 'config.logging.activity')); + } + } + + // check which dialog tab is default active + let enablePrivateTab = isTabTypeEnabled('private'); + let enableCorporationTab = isTabTypeEnabled('corporation'); + let enableAllianceTab = isTabTypeEnabled('alliance'); + + let activePrivateTab = false; + let activeCorporationTab = false; + let activeAllianceTab = false; + + if(enableCorporationTab){ + activeCorporationTab = true; + }else if(enableAllianceTab){ + activeAllianceTab = true; + }else if(enablePrivateTab){ + activePrivateTab = true; + } let data = { id: config.statsDialogId, dialogNavigationClass: config.dialogNavigationClass, dialogNavLiClass: config.dialogNavigationListItemClass, - enablePrivateTab: isTabTypeEnabled('private'), - enableCorporationTab: isTabTypeEnabled('corporation'), - enableAllianceTab: isTabTypeEnabled('alliance'), + enablePrivateTab: enablePrivateTab, + enableCorporationTab: enableCorporationTab, + enableAllianceTab: enableAllianceTab, + activePrivateTab: activePrivateTab, + activeCorporationTab: activeCorporationTab, + activeAllianceTab: activeAllianceTab, + logActivityEnabled: logActivityEnabled, statsContainerId: config.statsContainerId, statsTableId: config.statsTableId, dialogNavigationOffsetClass: config.dialogNavigationOffsetClass, @@ -670,7 +794,6 @@ define([ // model events statsDialog.on('show.bs.modal', function(e) { let dialogElement = $(e.target); - initStatsTable(dialogElement); }); diff --git a/public/js/v1.2.5/app/ui/dialog/system_effects.js b/public/js/v1.3.0/app/ui/dialog/system_effects.js similarity index 100% rename from public/js/v1.2.5/app/ui/dialog/system_effects.js rename to public/js/v1.3.0/app/ui/dialog/system_effects.js diff --git a/public/js/v1.2.5/app/ui/form_element.js b/public/js/v1.3.0/app/ui/form_element.js similarity index 97% rename from public/js/v1.2.5/app/ui/form_element.js rename to public/js/v1.3.0/app/ui/form_element.js index 649fa592..94d14e6b 100644 --- a/public/js/v1.2.5/app/ui/form_element.js +++ b/public/js/v1.3.0/app/ui/form_element.js @@ -197,15 +197,15 @@ define([ switch(options.type){ case 'character': - imagePath = Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg'; + imagePath = Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg'; previewContent = ''; break; case 'corporation': - imagePath = Init.url.ccpImageServer + 'Corporation/' + data.id + '_32.png'; + imagePath = Init.url.ccpImageServer + '/Corporation/' + data.id + '_32.png'; previewContent = ''; break; case 'alliance': - imagePath = Init.url.ccpImageServer + 'Alliance/' + data.id + '_32.png'; + imagePath = Init.url.ccpImageServer + '/Alliance/' + data.id + '_32.png'; previewContent = ''; break; } diff --git a/public/js/v1.2.5/app/ui/header.js b/public/js/v1.3.0/app/ui/header.js similarity index 100% rename from public/js/v1.2.5/app/ui/header.js rename to public/js/v1.3.0/app/ui/header.js diff --git a/public/js/v1.2.5/app/ui/logo.js b/public/js/v1.3.0/app/ui/logo.js similarity index 100% rename from public/js/v1.2.5/app/ui/logo.js rename to public/js/v1.3.0/app/ui/logo.js diff --git a/public/js/v1.2.5/app/ui/system_graph.js b/public/js/v1.3.0/app/ui/system_graph.js similarity index 84% rename from public/js/v1.2.5/app/ui/system_graph.js rename to public/js/v1.3.0/app/ui/system_graph.js index 4dba2c02..965f8dcb 100644 --- a/public/js/v1.2.5/app/ui/system_graph.js +++ b/public/js/v1.3.0/app/ui/system_graph.js @@ -10,7 +10,7 @@ define([ ], function($, Init, Util, Morris) { 'use strict'; - var config = { + let config = { // module info moduleClass: 'pf-module', // class for each module @@ -53,8 +53,8 @@ define([ * @param option * @returns {string} */ - var getInfoForGraph = function(graphKey, option){ - var info = ''; + let getInfoForGraph = function(graphKey, option){ + let info = ''; if(config.systemGraphLabels.hasOwnProperty(graphKey)){ info = config.systemGraphLabels[graphKey][option]; @@ -69,14 +69,14 @@ define([ * @param graphKey * @param graphData */ - var initGraph = function(graphElement, graphKey, graphData, eventLine){ + let initGraph = function(graphElement, graphKey, graphData, eventLine){ if(graphData.length > 0){ - var labelYFormat = function(y){ + let labelYFormat = function(y){ return Math.round(y); }; - var graphConfig = { + let graphConfig = { element: graphElement, data: graphData, xkey: 'x', @@ -121,24 +121,24 @@ define([ * @param parentElement * @param systemData */ - var drawModule = function(parentElement, systemData){ + let drawModule = function(parentElement, systemData){ // graph data is available for k-space systems if(systemData.type.id === 2){ - var requestData = { + let requestData = { systemIds: [systemData.systemId] }; // calculate time offset until system created - var serverData = Util.getServerTime(); + let serverData = Util.getServerTime(); - var timestampNow = Math.floor(serverData.getTime() / 1000); - var timeSinceUpdate = timestampNow - systemData.updated; + let timestampNow = Math.floor(serverData.getTime() / 1000); + let timeSinceUpdate = timestampNow - systemData.updated; - var timeInHours = Math.floor(timeSinceUpdate / 3600); - var timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60); - var timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2); - var eventLine = timeInHours + timeInMinutesPercent; + 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; @@ -152,7 +152,7 @@ define([ if( Object.keys(systemGraphsData).length > 0 ){ // create new (hidden) module container - var moduleElement = $('
', { + let moduleElement = $('
', { class: [config.moduleClass, config.systemGraphModuleClass].join(' '), css: {opacity: 0} }); @@ -165,7 +165,7 @@ define([ } // row element - var rowElement = $('
', { + let rowElement = $('
', { class: 'row' }); moduleElement.append(rowElement); @@ -173,15 +173,15 @@ define([ $.each(systemGraphsData, function(systemId, graphsData){ $.each(graphsData, function(graphKey, graphData){ - var colElement = $('
', { + let colElement = $('
', { class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ') }); - var headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); + let headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); colElement.append(headlineElement); - var graphElement = $('
', { + let graphElement = $('
', { class: config.systemGraphClass }); @@ -203,7 +203,7 @@ define([ }); } }).fail(function( jqXHR, status, error) { - var reason = status + ' ' + error; + let reason = status + ' ' + error; Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'}); $(document).setProgramStatus('problem'); }); @@ -218,10 +218,10 @@ define([ */ $.fn.drawSystemGraphModule = function(systemData){ - var parentElement = $(this); + let parentElement = $(this); // check if module already exists - var moduleElement = parentElement.find('.' + config.systemGraphModuleClass); + let moduleElement = parentElement.find('.' + config.systemGraphModuleClass); if(moduleElement.length > 0){ moduleElement.velocity('transition.slideDownOut', { diff --git a/public/js/v1.2.5/app/ui/system_info.js b/public/js/v1.3.0/app/ui/system_info.js similarity index 100% rename from public/js/v1.2.5/app/ui/system_info.js rename to public/js/v1.3.0/app/ui/system_info.js diff --git a/public/js/v1.2.5/app/ui/system_killboard.js b/public/js/v1.3.0/app/ui/system_killboard.js similarity index 93% rename from public/js/v1.2.5/app/ui/system_killboard.js rename to public/js/v1.3.0/app/ui/system_killboard.js index 32913f02..b58483b0 100644 --- a/public/js/v1.2.5/app/ui/system_killboard.js +++ b/public/js/v1.3.0/app/ui/system_killboard.js @@ -30,9 +30,10 @@ define([ }; /** - * get label element with given content + * * @param text - * @returns {*|XMLList} + * @param options + * @returns {jQuery} */ let getLabel = function(text, options){ let label = $('', { @@ -42,7 +43,6 @@ define([ return label; }; - let showKillmails = function(moduleElement, killboardData){ // show number of killMails @@ -61,7 +61,7 @@ define([ break; } - moduleElement.append( $('
').text(i + 'h ago')); + moduleElement.append( $('
').text( i ? i + 'h ago' : 'recent')); let killMailData = killboardData.tableData[i].killmails; @@ -77,25 +77,25 @@ define([ let killData = killMailData[j]; - let linkUrl = '//zkillboard.com/kill/' + killData.killID + '/'; - let victimImageUrl = Init.url.ccpImageServer + 'Type/' + killData.victim.shipTypeID + '_64.png'; - let killDate = getDateObjectByTimeString(killData.killTime); + let linkUrl = '//zkillboard.com/kill/' + killData.killmail_id + '/'; + let victimImageUrl = Init.url.ccpImageServer + '/Type/' + killData.victim.ship_type_id + '_64.png'; + let killDate = Util.convertDateToUTC(new Date(killData.killmail_time)); let killDateString = Util.convertDateToString(killDate); let killLossValue = Util.formatPrice( killData.zkb.totalValue ); // check for ally let victimAllyLogoUrl = ''; let displayAlly = 'none'; - if(killData.victim.allianceID > 0){ - victimAllyLogoUrl = Init.url.ccpImageServer + 'Alliance/' + killData.victim.allianceID + '_32.png'; + if(killData.victim.alliance_id > 0){ + victimAllyLogoUrl = Init.url.ccpImageServer + '/Alliance/' + killData.victim.alliance_id + '_32.png'; displayAlly = 'block'; } // check for corp let victimCorpLogoUrl = ''; let displayCorp = 'none'; - if(killData.victim.corporationID > 0){ - victimCorpLogoUrl = Init.url.ccpImageServer + 'Corporation/' + killData.victim.corporationID + '_32.png'; + if(killData.victim.corporation_id > 0){ + victimCorpLogoUrl = Init.url.ccpImageServer + '/Corporation/' + killData.victim.corporation_id + '_32.png'; displayCorp = 'inline'; } @@ -126,7 +126,7 @@ define([ text: killData.victim.characterName }).prepend( $('', { - text: killDateString + ' - ' + text: killDateString }) ).prepend( $('', { @@ -324,7 +324,7 @@ define([ wSpaceLinkModifier = 'w-space/'; } - let url = Init.url.zKillboard; + let url = Init.url.zKillboard + '/'; url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/'; killboardGraphElement.showLoadingAnimation(); @@ -338,16 +338,14 @@ define([ // the API wont return more than 200KMs ! - remember last bar block with complete KM information let lastCompleteDiffHourData = 0; - // loop kills and count kills by hour for (let i = 0; i < kbData.length; i++) { let killmailData = kbData[i]; - - let killDate = getDateObjectByTimeString(killmailData.killTime); + let killDate = Util.convertDateToUTC(new Date(killmailData.killmail_time)); // get time diff let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60); - let timeDiffHour = Math.round(timeDiffMin / 60); + let timeDiffHour = Math.floor(timeDiffMin / 60); // update chart data if (chartData[timeDiffHour]) { @@ -420,22 +418,11 @@ define([ }); }; - /** - * transform timestring - * @param timeString - * @returns {Date} - */ - let getDateObjectByTimeString = function(timeString){ - let match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/); - let date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]); - - return date; - }; - /** * get module element + * @param parentElement * @param systemData - * @returns {*|HTMLElement} + * @returns {*|jQuery|HTMLElement} */ let getModule = function(parentElement, systemData){ diff --git a/public/js/v1.2.5/app/ui/system_route.js b/public/js/v1.3.0/app/ui/system_route.js similarity index 100% rename from public/js/v1.2.5/app/ui/system_route.js rename to public/js/v1.3.0/app/ui/system_route.js diff --git a/public/js/v1.2.5/app/ui/system_signature.js b/public/js/v1.3.0/app/ui/system_signature.js similarity index 100% rename from public/js/v1.2.5/app/ui/system_signature.js rename to public/js/v1.3.0/app/ui/system_signature.js diff --git a/public/js/v1.2.5/app/util.js b/public/js/v1.3.0/app/util.js similarity index 96% rename from public/js/v1.2.5/app/util.js rename to public/js/v1.3.0/app/util.js index 58f4045f..350df026 100644 --- a/public/js/v1.2.5/app/util.js +++ b/public/js/v1.3.0/app/util.js @@ -4,8 +4,8 @@ define([ 'jquery', 'app/init', - 'config/system_effect', - 'config/signature_type', + 'conf/system_effect', + 'conf/signature_type', 'bootbox', 'localForage', 'velocity', @@ -264,7 +264,8 @@ define([ errors[i].field.length > 0 ){ let formField = formElement.find('[name="' + errors[i].field + '"]'); - formField.parents('.form-group').removeClass('has-success').addClass('has-error'); + let formGroup = formField.parents('.form-group').removeClass('has-success').addClass('has-error'); + let formHelp = formGroup.find('.help-block').text(errors[i].message); } }else if(errors[i].type === 'warning'){ @@ -559,6 +560,7 @@ define([ let data = { id: config.headCharacterSwitchId, + browserTabId: getBrowserTabId(), routes: Init.routes, userData: userData, otherCharacters: $.grep( userData.characters, function( character ) { @@ -729,17 +731,24 @@ define([ let defaultOptions = { dismissible: true, + messageId: 'pf-alert-' + Math.random().toString(36).substring(7), messageTypeClass: messageTypeClass, - messageTextClass: messageTextClass + messageTextClass: messageTextClass, + insertElement: 'replace' }; defaultOptions = $.extend(defaultOptions, config); - let content = Mustache.render(template, defaultOptions); - containerElement.html(content); + switch(defaultOptions.insertElement){ + case 'replace': containerElement.html(content); break; + case 'prepend': containerElement.prepend(content); break; + case 'append': containerElement.append(content); break; + default: console.error('insertElement: %s is not specified!', defaultOptions.insertElement); + } - containerElement.children().first().velocity('stop').velocity('fadeIn'); + //containerElement.children().first().velocity('stop').velocity('fadeIn'); + $('#' + defaultOptions.messageId).velocity('stop').velocity('fadeIn'); }); }; @@ -1072,6 +1081,20 @@ define([ return Init.currentUserData; }; + /** + * get a unique ID for each tab + * -> store ID in session storage + */ + let getBrowserTabId = () => { + let key = 'tabId'; + let tabId = sessionStorage.getItem(key); + if(tabId === null){ + tabId = Math.random().toString(36).substr(2, 5); + sessionStorage.setItem(key, tabId); + } + return tabId; + }; + /** * set default jQuery AJAX configuration */ @@ -1081,6 +1104,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(); @@ -2058,34 +2084,59 @@ define([ }; /** - * Create Date as UTC + * clear session Storage + * -> otherwise a tab refresh does not clear sessionStorage! + */ + let clearSessionStorage = () => { + if(sessionStorage){ + sessionStorage.clear(); + } + }; + + /** + * Create Date() as UTC * @param date * @returns {Date} */ - let createDateAsUTC = function(date) { + let createDateAsUTC = function(date){ return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); }; /** - * Convert Date to UTC (!important function!) + * Convert Date() to UTC (!important function!) * @param date * @returns {Date} */ - let convertDateToUTC = function(date) { + let convertDateToUTC = function(date){ return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()); }; /** - * Convert Date to Time String + * Convert Date() to Time String * @param date + * @param showSeconds * @returns {string} */ - let convertDateToString = function(date){ + let convertDateToString = function(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) : ''; return dateString + ' ' + timeString; }; + /** + * get deep json object value if exists + * -> e.g. key = 'first.last.third' string + * @param obj + * @param key + * @returns {*} + */ + let getObjVal = (obj, key) => { + return key.split('.').reduce((o, x) => { + return (typeof o === 'undefined' || o === null) ? o : o[x]; + }, obj); + }; + /** * get document path * -> www.pathfinder.com/pathfinder/ -> /pathfinder @@ -2197,11 +2248,15 @@ define([ getNearBySystemData: getNearBySystemData, getNearByCharacterData: getNearByCharacterData, setDestination: setDestination, + convertDateToUTC: convertDateToUTC, convertDateToString: convertDateToString, getOpenDialogs: getOpenDialogs, openIngameWindow: openIngameWindow, formatPrice: formatPrice, getLocalStorage: getLocalStorage, + clearSessionStorage: clearSessionStorage, + getBrowserTabId: getBrowserTabId, + getObjVal: getObjVal, getDocumentPath: getDocumentPath, redirect: redirect, logout: logout diff --git a/public/js/v1.2.5/app/worker/map.js b/public/js/v1.3.0/app/worker/map.js similarity index 100% rename from public/js/v1.2.5/app/worker/map.js rename to public/js/v1.3.0/app/worker/map.js diff --git a/public/js/v1.2.5/app/worker/message.js b/public/js/v1.3.0/app/worker/message.js similarity index 100% rename from public/js/v1.2.5/app/worker/message.js rename to public/js/v1.3.0/app/worker/message.js diff --git a/public/js/v1.2.5/lib/EasePack.min.js b/public/js/v1.3.0/lib/EasePack.min.js similarity index 100% rename from public/js/v1.2.5/lib/EasePack.min.js rename to public/js/v1.3.0/lib/EasePack.min.js diff --git a/public/js/v1.2.5/lib/TweenLite.min.js b/public/js/v1.3.0/lib/TweenLite.min.js similarity index 100% rename from public/js/v1.2.5/lib/TweenLite.min.js rename to public/js/v1.3.0/lib/TweenLite.min.js diff --git a/public/js/v1.2.5/lib/blueimp-gallery.js b/public/js/v1.3.0/lib/blueimp-gallery.js similarity index 100% rename from public/js/v1.2.5/lib/blueimp-gallery.js rename to public/js/v1.3.0/lib/blueimp-gallery.js diff --git a/public/js/v1.2.5/lib/blueimp-helper.js b/public/js/v1.3.0/lib/blueimp-helper.js similarity index 100% rename from public/js/v1.2.5/lib/blueimp-helper.js rename to public/js/v1.3.0/lib/blueimp-helper.js diff --git a/public/js/v1.2.5/lib/bootbox.min.js b/public/js/v1.3.0/lib/bootbox.min.js similarity index 68% rename from public/js/v1.2.5/lib/bootbox.min.js rename to public/js/v1.3.0/lib/bootbox.min.js index 0dc0cbd5..cb8edd0a 100644 --- a/public/js/v1.2.5/lib/bootbox.min.js +++ b/public/js/v1.3.0/lib/bootbox.min.js @@ -3,4 +3,4 @@ * * http://bootboxjs.com/license.txt */ -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.css("margin-top","-10px").prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.bootbox=b(a.jQuery)}(this,function a(b,c){"use strict";function d(a){var b=q[o.locale];return b?b[a]:q.en[a]}function e(a,c,d){a.stopPropagation(),a.preventDefault();var e=b.isFunction(d)&&d.call(c,a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},o,a),a.buttons||(a.buttons={}),c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c,d){var e={className:"bootbox-"+a,buttons:l.apply(null,b)};return m(j(e,d,c),b)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var n={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:"",textarea:"",email:"",select:"",checkbox:"
",date:"",time:"",number:"",password:""}},o={locale:"en",backdrop:"static",animate:!0,className:null,closeButton:!0,show:!0,container:"body"},p={};p.alert=function(){var a;if(a=k("alert",["ok"],["message","callback"],arguments),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback.call(this):!0},p.dialog(a)},p.confirm=function(){var a;if(a=k("confirm",["cancel","confirm"],["message","callback"],arguments),a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,!1)},a.buttons.confirm.callback=function(){return a.callback.call(this,!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return p.dialog(a)},p.prompt=function(){var a,d,e,f,h,i,k;if(f=b(n.form),d={className:"bootbox-prompt",buttons:l("cancel","confirm"),value:"",inputType:"text"},a=m(j(d,arguments,["title","callback"]),["cancel","confirm"]),i=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback.call(this,null)},a.buttons.confirm.callback=function(){var c;switch(a.inputType){case"text":case"textarea":case"email":case"select":case"date":case"time":case"number":case"password":c=h.val();break;case"checkbox":var d=h.find("input:checked");c=[],g(d,function(a,d){c.push(b(d).val())})}return a.callback.call(this,c)},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");if(!n.inputs[a.inputType])throw new Error("invalid prompt type");switch(h=b(n.inputs[a.inputType]),a.inputType){case"text":case"textarea":case"email":case"date":case"time":case"number":case"password":h.val(a.value);break;case"select":var o={};if(k=a.inputOptions||[],!b.isArray(k))throw new Error("Please pass an array of input options");if(!k.length)throw new Error("prompt with select requires options");g(k,function(a,d){var e=h;if(d.value===c||d.text===c)throw new Error("given options in wrong format");d.group&&(o[d.group]||(o[d.group]=b("").attr("label",d.group)),e=o[d.group]),e.append("")}),g(o,function(a,b){h.append(b)}),h.val(a.value);break;case"checkbox":var q=b.isArray(a.value)?a.value:[a.value];if(k=a.inputOptions||[],!k.length)throw new Error("prompt with checkbox requires options");if(!k[0].value||!k[0].text)throw new Error("given options in wrong format");h=b("
"),g(k,function(c,d){var e=b(n.inputs[a.inputType]);e.find("input").attr("value",d.value),e.find("label").append(d.text),g(q,function(a,b){b===d.value&&e.find("input").prop("checked",!0)}),h.append(e)})}return a.placeholder&&h.attr("placeholder",a.placeholder),a.pattern&&h.attr("pattern",a.pattern),a.maxlength&&h.attr("maxlength",a.maxlength),f.append(h),f.on("submit",function(a){a.preventDefault(),a.stopPropagation(),e.find(".btn-primary").click()}),e=p.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){h.focus()}),i===!0&&e.modal("show"),e},p.dialog=function(a){a=h(a);var d=b(n.dialog),f=d.find(".modal-dialog"),i=d.find(".modal-body"),j=a.buttons,k="",l={onEscape:a.onEscape};if(b.fn.modal===c)throw new Error("$.fn.modal is not defined; please double check you have included the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ for more details.");if(g(j,function(a,b){k+="",l[a]=b.callback}),i.find(".bootbox-body").html(a.message),a.animate===!0&&d.addClass("fade"),a.className&&d.addClass(a.className),"large"===a.size?f.addClass("modal-lg"):"small"===a.size&&f.addClass("modal-sm"),a.title&&i.before(n.header),a.closeButton){var m=b(n.closeButton);a.title?d.find(".modal-header").prepend(m):m.prependTo(i)}return a.title&&d.find(".modal-title").html(a.title),k.length&&(i.after(n.footer),d.find(".modal-footer").html(k)),d.on("hidden.bs.modal",function(a){a.target===this&&d.remove()}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),"static"!==a.backdrop&&d.on("click.dismiss.bs.modal",function(a){d.children(".modal-backdrop").length&&(a.currentTarget=d.children(".modal-backdrop").get(0)),a.target===a.currentTarget&&d.trigger("escape.close.bb")}),d.on("escape.close.bb",function(a){l.onEscape&&e(a,d,l.onEscape)}),d.on("click",".modal-footer button",function(a){var c=b(this).data("bb-handler");e(a,d,l[c])}),d.on("click",".bootbox-close-button",function(a){e(a,d,l.onEscape)}),d.on("keyup",function(a){27===a.which&&d.trigger("escape.close.bb")}),b(a.container).append(d),d.modal({backdrop:a.backdrop?"static":!1,keyboard:!1,show:!1}),a.show&&d.modal("show"),d},p.setDefaults=function(){var a={};2===arguments.length?a[arguments[0]]=arguments[1]:a=arguments[0],b.extend(o,a)},p.hideAll=function(){return b(".bootbox").modal("hide"),p};var q={bg_BG:{OK:"Ок",CANCEL:"Отказ",CONFIRM:"Потвърждавам"},br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},cs:{OK:"OK",CANCEL:"Zrušit",CONFIRM:"Potvrdit"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},el:{OK:"Εντάξει",CANCEL:"Ακύρωση",CONFIRM:"Επιβεβαίωση"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},et:{OK:"OK",CANCEL:"Katkesta",CONFIRM:"OK"},fa:{OK:"قبول",CANCEL:"لغو",CONFIRM:"تایید"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},he:{OK:"אישור",CANCEL:"ביטול",CONFIRM:"אישור"},hu:{OK:"OK",CANCEL:"Mégsem",CONFIRM:"Megerősít"},hr:{OK:"OK",CANCEL:"Odustani",CONFIRM:"Potvrdi"},id:{OK:"OK",CANCEL:"Batal",CONFIRM:"OK"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},ja:{OK:"OK",CANCEL:"キャンセル",CONFIRM:"確認"},lt:{OK:"Gerai",CANCEL:"Atšaukti",CONFIRM:"Patvirtinti"},lv:{OK:"Labi",CANCEL:"Atcelt",CONFIRM:"Apstiprināt"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},no:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},pt:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Confirmar"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},sq:{OK:"OK",CANCEL:"Anulo",CONFIRM:"Prano"},sv:{OK:"OK",CANCEL:"Avbryt",CONFIRM:"OK"},th:{OK:"ตกลง",CANCEL:"ยกเลิก",CONFIRM:"ยืนยัน"},tr:{OK:"Tamam",CANCEL:"İptal",CONFIRM:"Onayla"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return p.addLocale=function(a,c){return b.each(["OK","CANCEL","CONFIRM"],function(a,b){if(!c[b])throw new Error("Please supply a translation for '"+b+"'")}),q[a]={OK:c.OK,CANCEL:c.CANCEL,CONFIRM:c.CONFIRM},p},p.removeLocale=function(a){return delete q[a],p},p.setLocale=function(a){return p.setDefaults("locale",a)},p.init=function(c){return a(c||b)},p}); \ No newline at end of file diff --git a/public/js/v1.2.5/lib/bootstrap-confirmation.js b/public/js/v1.3.0/lib/bootstrap-confirmation.js similarity index 100% rename from public/js/v1.2.5/lib/bootstrap-confirmation.js rename to public/js/v1.3.0/lib/bootstrap-confirmation.js diff --git a/public/js/v1.2.5/lib/bootstrap-editable.min.js b/public/js/v1.3.0/lib/bootstrap-editable.min.js similarity index 100% rename from public/js/v1.2.5/lib/bootstrap-editable.min.js rename to public/js/v1.3.0/lib/bootstrap-editable.min.js diff --git a/public/js/v1.2.5/lib/bootstrap-image-gallery.js b/public/js/v1.3.0/lib/bootstrap-image-gallery.js similarity index 100% rename from public/js/v1.2.5/lib/bootstrap-image-gallery.js rename to public/js/v1.3.0/lib/bootstrap-image-gallery.js diff --git a/public/js/v1.2.5/lib/bootstrap.min.js b/public/js/v1.3.0/lib/bootstrap.min.js similarity index 100% rename from public/js/v1.2.5/lib/bootstrap.min.js rename to public/js/v1.3.0/lib/bootstrap.min.js diff --git a/public/js/v1.2.5/lib/bootstrap2-toggle.min.js b/public/js/v1.3.0/lib/bootstrap2-toggle.min.js similarity index 100% rename from public/js/v1.2.5/lib/bootstrap2-toggle.min.js rename to public/js/v1.3.0/lib/bootstrap2-toggle.min.js diff --git a/public/js/v1.2.5/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js b/public/js/v1.3.0/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js similarity index 100% rename from public/js/v1.2.5/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js rename to public/js/v1.3.0/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js diff --git a/public/js/v1.2.5/lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min.js b/public/js/v1.3.0/lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min.js similarity index 100% rename from public/js/v1.2.5/lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min.js rename to public/js/v1.3.0/lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min.js diff --git a/public/js/v1.2.5/lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min.js b/public/js/v1.3.0/lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min.js similarity index 100% rename from public/js/v1.2.5/lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min.js rename to public/js/v1.3.0/lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min.js diff --git a/public/js/v1.2.5/lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min.js b/public/js/v1.3.0/lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min.js similarity index 100% rename from public/js/v1.2.5/lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min.js rename to public/js/v1.3.0/lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min.js diff --git a/public/js/v1.2.5/lib/datatables/Select-1.2.0/js/dataTables.select.min.js b/public/js/v1.3.0/lib/datatables/Select-1.2.0/js/dataTables.select.min.js similarity index 100% rename from public/js/v1.2.5/lib/datatables/Select-1.2.0/js/dataTables.select.min.js rename to public/js/v1.3.0/lib/datatables/Select-1.2.0/js/dataTables.select.min.js diff --git a/public/js/v1.2.5/lib/dom.jsPlumb-1.7.6.js b/public/js/v1.3.0/lib/dom.jsPlumb-1.7.6.js similarity index 100% rename from public/js/v1.2.5/lib/dom.jsPlumb-1.7.6.js rename to public/js/v1.3.0/lib/dom.jsPlumb-1.7.6.js diff --git a/public/js/v1.2.5/lib/farahey-0.5.js b/public/js/v1.3.0/lib/farahey-0.5.js similarity index 100% rename from public/js/v1.2.5/lib/farahey-0.5.js rename to public/js/v1.3.0/lib/farahey-0.5.js diff --git a/public/js/v1.2.5/lib/jquery-3.1.1.min.js b/public/js/v1.3.0/lib/jquery-3.1.1.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery-3.1.1.min.js rename to public/js/v1.3.0/lib/jquery-3.1.1.min.js diff --git a/public/js/v1.2.5/lib/jquery.dragToSelect.js b/public/js/v1.3.0/lib/jquery.dragToSelect.js similarity index 94% rename from public/js/v1.2.5/lib/jquery.dragToSelect.js rename to public/js/v1.3.0/lib/jquery.dragToSelect.js index e37756fb..98937ab4 100644 --- a/public/js/v1.2.5/lib/jquery.dragToSelect.js +++ b/public/js/v1.3.0/lib/jquery.dragToSelect.js @@ -93,13 +93,26 @@ jQuery.fn.dragToSelect = function (conf) { return this; } - var parentOffset = parent.offset(); - var parentDim = { - left: parentOffset.left, - top: parentOffset.top, - width: parent.width(), - height: parent.height() - }; + var parentDim = { + left: 0, + top: 0, + width: 10, + height: 10 + }; + + // set parent dimensions + // -> should be updated in case of left/right menu is open + var setParentDimensions = (parent) => { + var parentOffset = parent.offset(); + parentDim = { + left: parentOffset.left, + top: parentOffset.top, + width: parent.width(), + height: parent.height() + }; + } + + setParentDimensions(parent); // Current origin of select box var selectBoxOrigin = { @@ -343,6 +356,7 @@ jQuery.fn.dragToSelect = function (conf) { // Do the right stuff then return this -------------------------------------------------------- selectBox.mousemove(function(e){ + setParentDimensions(parent); lastMousePosition.x = e.pageX; lastMousePosition.y = e.pageY; e.preventDefault(); @@ -353,7 +367,6 @@ jQuery.fn.dragToSelect = function (conf) { e.which === 1 && // left mouse down 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()) { return; @@ -366,6 +379,7 @@ jQuery.fn.dragToSelect = function (conf) { e.preventDefault(); }).mousemove(function(e){ + setParentDimensions(parent); lastMousePosition.x = e.pageX; lastMousePosition.y = e.pageY; e.preventDefault(); diff --git a/public/js/v1.2.5/lib/jquery.easypiechart.min.js b/public/js/v1.3.0/lib/jquery.easypiechart.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.easypiechart.min.js rename to public/js/v1.3.0/lib/jquery.easypiechart.min.js diff --git a/public/js/v1.2.5/lib/jquery.fullscreen.min.js b/public/js/v1.3.0/lib/jquery.fullscreen.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.fullscreen.min.js rename to public/js/v1.3.0/lib/jquery.fullscreen.min.js diff --git a/public/js/v1.2.5/lib/jquery.hoverIntent.minified.js b/public/js/v1.3.0/lib/jquery.hoverIntent.minified.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.hoverIntent.minified.js rename to public/js/v1.3.0/lib/jquery.hoverIntent.minified.js diff --git a/public/js/v1.2.5/lib/jquery.lazylinepainter-1.5.1.min.js b/public/js/v1.3.0/lib/jquery.lazylinepainter-1.5.1.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.lazylinepainter-1.5.1.min.js rename to public/js/v1.3.0/lib/jquery.lazylinepainter-1.5.1.min.js diff --git a/public/js/v1.2.5/lib/jquery.lazyload.min.js b/public/js/v1.3.0/lib/jquery.lazyload.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.lazyload.min.js rename to public/js/v1.3.0/lib/jquery.lazyload.min.js diff --git a/public/js/v1.2.5/lib/jquery.mCustomScrollbar.min.js b/public/js/v1.3.0/lib/jquery.mCustomScrollbar.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.mCustomScrollbar.min.js rename to public/js/v1.3.0/lib/jquery.mCustomScrollbar.min.js diff --git a/public/js/v1.2.5/lib/jquery.mousewheel.min.js b/public/js/v1.3.0/lib/jquery.mousewheel.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.mousewheel.min.js rename to public/js/v1.3.0/lib/jquery.mousewheel.min.js diff --git a/public/js/v1.2.5/lib/jquery.peity.min.js b/public/js/v1.3.0/lib/jquery.peity.min.js similarity index 100% rename from public/js/v1.2.5/lib/jquery.peity.min.js rename to public/js/v1.3.0/lib/jquery.peity.min.js diff --git a/public/js/v1.2.5/lib/jsPlumb-2.0.5.js b/public/js/v1.3.0/lib/jsPlumb-2.0.5.js similarity index 100% rename from public/js/v1.2.5/lib/jsPlumb-2.0.5.js rename to public/js/v1.3.0/lib/jsPlumb-2.0.5.js diff --git a/public/js/v1.2.5/lib/localforage.min.js b/public/js/v1.3.0/lib/localforage.min.js similarity index 100% rename from public/js/v1.2.5/lib/localforage.min.js rename to public/js/v1.3.0/lib/localforage.min.js diff --git a/public/js/v1.2.5/lib/morris.min.js b/public/js/v1.3.0/lib/morris.min.js similarity index 100% rename from public/js/v1.2.5/lib/morris.min.js rename to public/js/v1.3.0/lib/morris.min.js diff --git a/public/js/v1.2.5/lib/mustache.min.js b/public/js/v1.3.0/lib/mustache.min.js similarity index 100% rename from public/js/v1.2.5/lib/mustache.min.js rename to public/js/v1.3.0/lib/mustache.min.js diff --git a/public/js/v1.2.5/lib/pnotify/pnotify.buttons.js b/public/js/v1.3.0/lib/pnotify/pnotify.buttons.js similarity index 100% rename from public/js/v1.2.5/lib/pnotify/pnotify.buttons.js rename to public/js/v1.3.0/lib/pnotify/pnotify.buttons.js diff --git a/public/js/v1.2.5/lib/pnotify/pnotify.callbacks.js b/public/js/v1.3.0/lib/pnotify/pnotify.callbacks.js similarity index 100% rename from public/js/v1.2.5/lib/pnotify/pnotify.callbacks.js rename to public/js/v1.3.0/lib/pnotify/pnotify.callbacks.js diff --git a/public/js/v1.2.5/lib/pnotify/pnotify.desktop.js b/public/js/v1.3.0/lib/pnotify/pnotify.desktop.js similarity index 100% rename from public/js/v1.2.5/lib/pnotify/pnotify.desktop.js rename to public/js/v1.3.0/lib/pnotify/pnotify.desktop.js diff --git a/public/js/v1.2.5/lib/pnotify/pnotify.js b/public/js/v1.3.0/lib/pnotify/pnotify.js similarity index 100% rename from public/js/v1.2.5/lib/pnotify/pnotify.js rename to public/js/v1.3.0/lib/pnotify/pnotify.js diff --git a/public/js/v1.2.5/lib/pnotify/pnotify.nonblock.js b/public/js/v1.3.0/lib/pnotify/pnotify.nonblock.js similarity index 100% rename from public/js/v1.2.5/lib/pnotify/pnotify.nonblock.js rename to public/js/v1.3.0/lib/pnotify/pnotify.nonblock.js diff --git a/public/js/v1.2.5/lib/raphael-min.js b/public/js/v1.3.0/lib/raphael-min.js similarity index 100% rename from public/js/v1.2.5/lib/raphael-min.js rename to public/js/v1.3.0/lib/raphael-min.js diff --git a/public/js/v1.2.5/lib/require.js b/public/js/v1.3.0/lib/require.js similarity index 100% rename from public/js/v1.2.5/lib/require.js rename to public/js/v1.3.0/lib/require.js diff --git a/public/js/v1.2.5/lib/requirejs/text.js b/public/js/v1.3.0/lib/requirejs/text.js similarity index 100% rename from public/js/v1.2.5/lib/requirejs/text.js rename to public/js/v1.3.0/lib/requirejs/text.js diff --git a/public/js/v1.2.5/lib/select2.min.js b/public/js/v1.3.0/lib/select2.min.js similarity index 100% rename from public/js/v1.2.5/lib/select2.min.js rename to public/js/v1.3.0/lib/select2.min.js diff --git a/public/js/v1.2.5/lib/slidebars.js b/public/js/v1.3.0/lib/slidebars.js similarity index 100% rename from public/js/v1.2.5/lib/slidebars.js rename to public/js/v1.3.0/lib/slidebars.js diff --git a/public/js/v1.3.0/lib/validator.min.js b/public/js/v1.3.0/lib/validator.min.js new file mode 100644 index 00000000..71916ad1 --- /dev/null +++ b/public/js/v1.3.0/lib/validator.min.js @@ -0,0 +1,9 @@ +/*! + * Validator v0.11.9 for Bootstrap 3, by @1000hz + * Copyright 2017 Cina Saffary + * Licensed under http://opensource.org/licenses/MIT + * + * https://github.com/1000hz/bootstrap-validator + */ + ++function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:b.is("select[multiple]")?(b.val()||[]).length:b.val()}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.validators=a.extend({},d.VALIDATORS,e.custom),this.$element=a(c),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),this.update(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.on("reset.bs.validator",a.proxy(this.reset,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.attr("data-match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})}),this.$inputs.filter(function(){return b(a(this))&&!a(this).closest(".has-error").length}).trigger("focusout"),this.$element.attr("novalidate",!0)};d.VERSION="0.11.9",d.INPUT_SELECTOR=':input:not([type="hidden"], [type="submit"], [type="reset"], button)',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?!b.checkValidity()&&!b.validity.valid&&(b.validationMessage||"error!"):void 0},match:function(b){var c=b.attr("data-match");return b.val()!==a(c).val()&&d.DEFAULTS.errors.match},minlength:function(a){var b=a.attr("data-minlength");return a.val().length").addClass("list-unstyled").append(a.map(d,function(b){return a("
  • ")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger has-success"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&e.removeClass(this.options.feedback.success)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){var c=b(a(this));return!("string"==typeof c?a.trim(c):c)}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),(this.isIncomplete()||this.hasErrors())&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.reset=function(){return this.$element.find(".form-control-feedback").removeClass(this.options.feedback.error).removeClass(this.options.feedback.success),this.$inputs.removeData(["bs.validator.errors","bs.validator.deferred"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$btn.removeClass("disabled"),this.$element.find(".has-error, .has-danger, .has-success").removeClass("has-error has-danger has-success"),this},d.prototype.destroy=function(){return this.reset(),this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator"),this.$inputs.off(".bs.validator"),this.options=null,this.validators=null,this.$element=null,this.$btn=null,this.$inputs=null,this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery); \ No newline at end of file diff --git a/public/js/v1.2.5/lib/velocity.min.js b/public/js/v1.3.0/lib/velocity.min.js similarity index 100% rename from public/js/v1.2.5/lib/velocity.min.js rename to public/js/v1.3.0/lib/velocity.min.js diff --git a/public/js/v1.2.5/lib/velocity.ui.min.js b/public/js/v1.3.0/lib/velocity.ui.min.js similarity index 100% rename from public/js/v1.2.5/lib/velocity.ui.min.js rename to public/js/v1.3.0/lib/velocity.ui.min.js diff --git a/public/templates/dialog/map.html b/public/templates/dialog/map.html index bf9ca0a7..fb3e5801 100644 --- a/public/templates/dialog/map.html +++ b/public/templates/dialog/map.html @@ -1,7 +1,7 @@
    -
  • -
    Drop map file here
    +
    Drop file here
    @@ -244,7 +379,7 @@ diff --git a/public/templates/dialog/map_info.html b/public/templates/dialog/map_info.html index d32ca796..11173eb4 100644 --- a/public/templates/dialog/map_info.html +++ b/public/templates/dialog/map_info.html @@ -11,6 +11,11 @@  Activity +
  • + +  Log  [BETA] + +
  • @@ -35,7 +40,7 @@ Your browser window is to small. Resize it to obtain more columns.
    -

    Map

    +

    Map

    @@ -47,15 +52,39 @@

    Connections

    -
    -
    +
    {{! "users" tab -------------------------------------------------------- }}

    Active pilots

    -
    +
    +
    + + {{! "log" tab ---------------------------------------------------------- }} +
    +

    Log viewer

    + +
    + +
    +
    +
    +
    +
    Map settings:
    +
    + Save map changes to logfile  + {{#logHistoryEnabled}} + enabled + {{/logHistoryEnabled}} + {{^logHistoryEnabled}} + disabled + {{/logHistoryEnabled}} +
    +
    +
    +
    diff --git a/public/templates/dialog/map_manual.html b/public/templates/dialog/map_manual.html index 02bb6ef7..f6c3d1e6 100644 --- a/public/templates/dialog/map_manual.html +++ b/public/templates/dialog/map_manual.html @@ -1,19 +1,21 @@
    -

      Map

    +

      Map

    Map types

    @@ -43,7 +45,7 @@

    If "map tracking" is on, new systems (more) and connections (more) - will be automatically added to the current map (more). + will be automatically added to the current map (more). This can only work if your current position is known by pathfinder.
    The map scope (more) defines which systems are affected.
    The connection scope (more) will be auto-detected (e.g. wormhole connection). @@ -54,10 +56,10 @@ right click somewhere on the map to open the context menu.

      -
    • Show some basic map information
    • Add a new system at the position, you clicked at
    • Select all (unlocked) systems on the map
    • Filter map connections by a scope more
    • +
    • Map related options (edit map, information)
    • Delete selected systems more

    Select methods

    @@ -70,12 +72,12 @@

    Grid snapping

    - The "Grid snapping" option can be activated on the "Map Menu" (). Systems will snap to a 20x20px grid. + The "Grid snapping" option can be activated on the "Map Menu" (). Systems will snap to a 20x20px grid. A grid overlay appears on the map.

    Magnetizing

    - The "Magnetizing" option can be activated on the "Map Menu" (). + The "Magnetizing" option can be activated on the "Map Menu" (). If active, systems effect each other while move/drag&drop and will rearrange themselves automatically on the map. This prevents system "overlapping"

    Update counter

    @@ -97,7 +99,7 @@

      Solar system / Wormhole system

    - Systems are represented by rectangle boxes on a map (more). + Systems are represented by rectangle boxes on a map (more). Pilots can interact with systems like "delete systems" (more) or "move systems" (more).

    @@ -173,7 +175,7 @@
    • Adds a new system that connects to this system
    • Lock this system more
    • -
    • Set "Rally Point" for this system more
    • +
    • Set "Rally Point" for this system more
    • Changes the status of this system more
    • Waypoint options for this system more
    • Delete this system and all connections more
    • @@ -184,12 +186,17 @@ moved (more) or deleted (more).

      -

      Rally point

      +

      Rally point

      - Systems can be marked as a Rally point.
      - Active pilot will be informed about new "Rally points" by a notice. Optional you can poke active pilots with "desktop push notifications" - (more). + Systems can be marked as Rally point. + When a new "Rally point" is set, you can optional enter a message and set various "Poke" options, in order to send notifications to other active pilots.
      + Slack and Email pokes require a valid map configuration (map settings dialog) before they can be used.

      +
        +
      • Desktop poke - (send OS browser popup messages)
      • +
      • Slack poke - (send poke message to a Slack channel)
      • +
      • Email poke - (send a mail with your poke message)
      • +

      Waypoints

      Waypoints can be set to systems. Waypoint options are identical to their in game options. @@ -339,7 +346,7 @@

      The concept of map sharing is pretty complex and powerful. By default all sharing options are disabled, so there is no reason to be concerned about map security. - All map types (more) can be temporary shared with allied entities e.g. for "Joint-Ops".
      + All map types (more) can be temporary shared with allied entities e.g. for "Joint-Ops".
      The map type is preserved for the period of sharing (private maps can be shared with other users, corporation maps can be shared with other corporations,..). Do the following steps to share your map with your friend/allied corporation/allied alliance:

      @@ -400,7 +407,7 @@

      Types

      Success (success message after pilot interaction)
      -
      Information (non-important additional information)
      +
      Info (non-important additional information)
      Warning (important non-critical information)
      Error (very important information of error)
      @@ -411,10 +418,37 @@ Events that trigger desktop notification.

        -
      • New Rally point ( more)
      • +
      • New Rally point ( more)

      {{! ================================================================================================================================================ }} +

        Statistics

      +

      + pathfinder can log user statistics for each map. Activate User statistics for a map in the map settings dialog.
      + User statistics can be globally disabled for some map types on this installation. + In order to provide the pilot with useful information about his environment, data visualisation is a good way to compress the amount of relevant data. +

      + +
      + {{! ================================================================================================================================================ }} + +

        Logging

      +

      + Just as the "Statistics" logging (more), which is primarily useful for character comparison, + pathfinder logs a lot more useful stuff "under the hood". Some of these map specific logs can also be seen by the users.
      + Map specific log settings can be changed for each map individually; see map settings dialog ().
      + Some log methods might be globally disabled for some map types (more). +

      +

      Write log files

      +

      + When enabled, pathfinder will write a physical log file with map related events. There is UI in the "map information" dialog available (log file viewer). +

      +

      Send logs to a Slack channel

      +

      + Map logs can also be send to a Slack channel. This requires a valid "WebHook" configuration for your map.
      + Slack´s Webhook API has bad response times, use this feature with caution! It can slow down other map updates. +

      +
    diff --git a/public/templates/dialog/settings.html b/public/templates/dialog/settings.html index c0b66058..44267dbd 100644 --- a/public/templates/dialog/settings.html +++ b/public/templates/dialog/settings.html @@ -54,7 +54,7 @@
    -
    +
    @@ -91,7 +91,7 @@
    -
    +
    @@ -106,7 +106,7 @@ -
    +
    @@ -139,7 +139,7 @@ -
    +
    @@ -173,7 +173,7 @@ {{#userData.character}}

      Private maps "{{name}}"

    -
    +
    + + @@ -89,6 +91,10 @@ + + + + @@ -109,11 +115,35 @@ + + + +
    character
    + +
    +
    +
    +
    +
    Map settings:
    +
    + Store user statistics  + {{#logActivityEnabled}} + enabled + {{/logActivityEnabled}} + {{^logActivityEnabled}} + disabled + {{/logActivityEnabled}} +
    +
    +
    +
    +
    +
    diff --git a/public/templates/dialog/system.html b/public/templates/dialog/system.html index 085e9f82..7dfbff4b 100644 --- a/public/templates/dialog/system.html +++ b/public/templates/dialog/system.html @@ -31,7 +31,7 @@
    - + diff --git a/public/templates/dialog/system_rally.html b/public/templates/dialog/system_rally.html index 105f3c77..cc24db66 100644 --- a/public/templates/dialog/system_rally.html +++ b/public/templates/dialog/system_rally.html @@ -1,17 +1,80 @@ -Do you want poke active pilots? -
    -

    - - Desktop notification is - enabled. -

    -

    - - Mail notification is - {{#notificationStatus}} - enabled. - {{/notificationStatus}} - {{^notificationStatus}} - disabled. - {{/notificationStatus}} -

    +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + + + + + +
    +
    +
    +
    +
    Map settings:
    +
    + Desktop poke  + {{#desktopRallyEnabled}} + enabled + {{/desktopRallyEnabled}} + {{^desktopRallyEnabled}} + disabled + {{/desktopRallyEnabled}} +
    +
    + Slack poke  + {{#slackRallyEnabled}} + enabled + {{/slackRallyEnabled}} + {{^slackRallyEnabled}} + disabled + {{/slackRallyEnabled}} +
    +
    + Mail poke  + {{#mailRallyEnabled}} + enabled + {{/mailRallyEnabled}} + {{^mailRallyEnabled}} + disabled + {{/mailRallyEnabled}} +
    +
    +
    + +
    +
    + +
    \ No newline at end of file diff --git a/public/templates/form/map_settings.html b/public/templates/form/map.html similarity index 89% rename from public/templates/form/map_settings.html rename to public/templates/form/map.html index 51594bae..6b6b2571 100644 --- a/public/templates/form/map_settings.html +++ b/public/templates/form/map.html @@ -1,7 +1,7 @@
    -
    +
    @@ -13,19 +13,19 @@
    -
    +
    - Choose a meaningful name + Choose a meaningful name
    -
    +
    @@ -37,7 +37,7 @@
    -
    +
    @@ -55,7 +55,7 @@ diff --git a/public/templates/form/message.html b/public/templates/form/message.html index 3e07652a..2276c177 100644 --- a/public/templates/form/message.html +++ b/public/templates/form/message.html @@ -1,4 +1,4 @@ -