diff --git a/.gitignore b/.gitignore index 706e59c..176ece1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ #Ignore files -admin/red-config.php .htaccess +app/config.php -#Ignored folders -admin/db/db_options -admin/db/db_records -admin/db/db_users \ No newline at end of file +#Ignored folders \ No newline at end of file diff --git a/admin/db/vendor/autoload.php b/admin/db/vendor/autoload.php deleted file mode 100644 index f68f62d..0000000 --- a/admin/db/vendor/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; -defined('ABSPATH') or die('No script kiddies please!'); - -/** - * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ - */ -class ClassLoader -{ - // PSR-4 - private $prefixLengthsPsr4 = array(); - private $prefixDirsPsr4 = array(); - private $fallbackDirsPsr4 = array(); - - // PSR-0 - private $prefixesPsr0 = array(); - private $fallbackDirsPsr0 = array(); - - private $useIncludePath = false; - private $classMap = array(); - private $classMapAuthoritative = false; - private $missingClasses = array(); - private $apcuPrefix; - - public function getPrefixes() - { - if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); - } - - return array(); - } - - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - (array) $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - (array) $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories - * - * @throws \InvalidArgumentException - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - (array) $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - (array) $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * - * @throws \InvalidArgumentException - */ - public function setPsr4($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Turns off searching the prefix and fallback directories for classes - * that have not been registered with the class map. - * - * @param bool $classMapAuthoritative - */ - public function setClassMapAuthoritative($classMapAuthoritative) - { - $this->classMapAuthoritative = $classMapAuthoritative; - } - - /** - * Should class lookup fail if not found in the current class map? - * - * @return bool - */ - public function isClassMapAuthoritative() - { - return $this->classMapAuthoritative; - } - - /** - * APCu prefix to use to cache found/not-found classes, if the extension is enabled. - * - * @param string|null $apcuPrefix - */ - public function setApcuPrefix($apcuPrefix) - { - $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; - } - - /** - * The APCu prefix in use, or null if APCu caching is not enabled. - * - * @return string|null - */ - public function getApcuPrefix() - { - return $this->apcuPrefix; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - includeFile($file); - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { - return false; - } - if (null !== $this->apcuPrefix) { - $file = apcu_fetch($this->apcuPrefix.$class, $hit); - if ($hit) { - return $file; - } - } - - $file = $this->findFileWithExtension($class, '.php'); - - // Search for Hack files if we are running on HHVM - if (false === $file && defined('HHVM_VERSION')) { - $file = $this->findFileWithExtension($class, '.hh'); - } - - if (null !== $this->apcuPrefix) { - apcu_add($this->apcuPrefix.$class, $file); - } - - if (false === $file) { - // Remember that this class does not exist. - $this->missingClasses[$class] = true; - } - - return $file; - } - - private function findFileWithExtension($class, $ext) - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - $subPath = $class; - while (false !== $lastPos = strrpos($subPath, '\\')) { - $subPath = substr($subPath, 0, $lastPos); - $search = $subPath . '\\'; - if (isset($this->prefixDirsPsr4[$search])) { - $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); - foreach ($this->prefixDirsPsr4[$search] as $dir) { - if (file_exists($file = $dir . $pathEnd)) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - - return false; - } -} - -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; -} diff --git a/admin/db/vendor/composer/LICENSE b/admin/db/vendor/composer/LICENSE deleted file mode 100644 index f27399a..0000000 --- a/admin/db/vendor/composer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - -Copyright (c) Nils Adermann, Jordi Boggiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/admin/db/vendor/composer/autoload_classmap.php b/admin/db/vendor/composer/autoload_classmap.php deleted file mode 100644 index 7fef243..0000000 --- a/admin/db/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,9 +0,0 @@ - array($vendorDir . '/tmarois/filebase/src'), -); diff --git a/admin/db/vendor/composer/autoload_real.php b/admin/db/vendor/composer/autoload_real.php deleted file mode 100644 index 811afa9..0000000 --- a/admin/db/vendor/composer/autoload_real.php +++ /dev/null @@ -1,52 +0,0 @@ -= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInitdfe443be72da0df5335a79abfe924060::getInitializer($loader)); - } else { - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } - - $loader->register(true); - - return $loader; - } -} diff --git a/admin/db/vendor/composer/autoload_static.php b/admin/db/vendor/composer/autoload_static.php deleted file mode 100644 index 8a7fe04..0000000 --- a/admin/db/vendor/composer/autoload_static.php +++ /dev/null @@ -1,32 +0,0 @@ - - array ( - 'Filebase\\' => 9, - ), - ); - - public static $prefixDirsPsr4 = array ( - 'Filebase\\' => - array ( - 0 => __DIR__ . '/..' . '/tmarois/filebase/src', - ), - ); - - public static function getInitializer(ClassLoader $loader) - { - return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInitdfe443be72da0df5335a79abfe924060::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInitdfe443be72da0df5335a79abfe924060::$prefixDirsPsr4; - - }, null, ClassLoader::class); - } -} diff --git a/admin/db/vendor/tmarois/filebase/LICENSE b/admin/db/vendor/tmarois/filebase/LICENSE deleted file mode 100644 index def1fd9..0000000 --- a/admin/db/vendor/tmarois/filebase/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Timothy Marois - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/admin/db/vendor/tmarois/filebase/src/Backup.php b/admin/db/vendor/tmarois/filebase/src/Backup.php deleted file mode 100644 index 57ae5e9..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Backup.php +++ /dev/null @@ -1,192 +0,0 @@ -backupLocation = $backupLocation; - $this->config = $database->getConfig(); - $this->database = $database; - - // Check directory and create it if it doesn't exist - if (!is_dir($this->backupLocation)) - { - if (!@mkdir($this->backupLocation, 0777, true)) - { - throw new \Exception(sprintf('`%s` doesn\'t exist and can\'t be created.', $this->backupLocation)); - } - } - else if (!is_writable($this->backupLocation)) - { - throw new \Exception(sprintf('`%s` is not writable.', $this->backupLocation)); - } - } - - /** - * save() - * - */ - public function create() - { - $backupFile = $this->backupLocation.'/'.time().'.zip'; - - if ($results = $this->zip($this->config->dir, $backupFile)) - { - $basename = basename($backupFile); - return $basename; - } - - throw new \Exception('Error backing up database.'); - } - - /** - * find() - * - * Returns an array of all the backups currently available - * - */ - public function find() - { - $backups = []; - $files = glob(realpath($this->backupLocation)."/*.zip"); - foreach($files as $file) - { - $basename = str_replace('.zip','',basename($file)); - $backups[$basename] = $this->backupLocation.'/'.$basename.'.zip'; - } - - krsort($backups); - - return $backups; - } - - /** - * clean() - * - * Clears and deletes all backups (zip files only) - * - */ - public function clean() - { - return array_map('unlink', glob(realpath($this->backupLocation)."/*.zip")); - } - - /** - * rollback() - * - * Rollback database to the last backup available - * - */ - public function rollback() - { - $backups = $this->find(); - $restore = current($backups); - - $this->database->truncate(); - - return $this->extract($restore, $this->config->dir); - } - - /** - * extract() - * - * @param string $source (zip location) - * @param string $target (unload files to location) - * @return bool - */ - protected function extract($source = '', $target = '') - { - if (!extension_loaded('zip') && !file_exists($source)) - { - return false; - } - $zip = new \ZipArchive(); - if ($zip->open($source) === TRUE) - { - $zip->extractTo($target); - $zip->close(); - - return true; - } - return false; - } - - /** - * zip() - * - * Prevents the zip from zipping up the storage diretories - * - */ - protected function zip($source = '', $target = '') - { - if (!extension_loaded('zip') || !file_exists($source)) - { - return false; - } - - $zip = new \ZipArchive(); - if (!$zip->open($target, \ZIPARCHIVE::CREATE)) - { - $zip->addFromString(basename($source), file_get_contents($source)); - } - $source = realpath($source); - if (is_dir($source)) - { - $iterator = new \RecursiveDirectoryIterator($source); - $iterator->setFlags(\RecursiveDirectoryIterator::SKIP_DOTS); - $files = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); - foreach ($files as $file) - { - $file = realpath($file); - - if (preg_match('|'.realpath($this->backupLocation).'|',$file)) - { - continue; - } - - if (is_dir($file)) - { - $zip->addEmptyDir(str_replace($source . '/', '', $file . '/')); - } - else if (is_file($file)) - { - $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file)); - } - } - - } - - return $zip->close(); - - } - -} diff --git a/admin/db/vendor/tmarois/filebase/src/Cache.php b/admin/db/vendor/tmarois/filebase/src/Cache.php deleted file mode 100644 index 7eaa710..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Cache.php +++ /dev/null @@ -1,145 +0,0 @@ -database = $database; - - $this->cache_database = new \Filebase\Database([ - 'dir' => $this->database->getConfig()->dir.'/__cache', - 'cache' => false, - 'pretty' => false - ]); - } - - /** - * setKey() - * - * This key is used to identify the cache - * and know how to call the cache again - * - */ - public function setKey($key) - { - $this->key = md5($key); - } - - /** - * getKey() - * - */ - public function getKey() - { - return $this->key; - } - - /** - * flush() - * - */ - public function flush() - { - $this->cache_database->flush(true); - } - - /** - * expired() - * - * @param $time (date format) - * @return bool (true/false) - */ - public function expired($time) - { - if ( (strtotime($time)+$this->database->getConfig()->cache_expires) > time() ) - { - return false; - } - - return true; - } - - /** - * getDocuments() - * - */ - public function getDocuments($documents) - { - $d = []; - foreach($documents as $document) - { - $d[] = $this->database->get($document)->setFromCache(true); - } - - return $d; - } - - /** - * get() - * - */ - public function get() - { - if (!$this->getKey()) - { - throw new \Exception('You must supply a cache key using setKey to get cache data.'); - } - - $cache_doc = $this->cache_database->get( $this->getKey() ); - - if (!$cache_doc->toArray()) - { - return false; - } - - if ( $this->expired( $cache_doc->updatedAt() ) ) - { - return false; - } - - return $this->getDocuments($cache_doc->toArray()); - } - - /** - * store() - * - */ - public function store($data) - { - if (!$this->getKey()) - { - throw new \Exception('You must supply a cache key using setKey to store cache data.'); - } - - return $this->cache_database->get( $this->getKey() )->set($data)->save(); - } - -} diff --git a/admin/db/vendor/tmarois/filebase/src/Config.php b/admin/db/vendor/tmarois/filebase/src/Config.php deleted file mode 100644 index 56c2d3b..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Config.php +++ /dev/null @@ -1,125 +0,0 @@ - $value) - { - $this->{$key} = $value; - } - - // if "backupLocation" is not set, let's set one automatically - if (!isset($config['backupLocation'])) - { - $this->backupLocation = $this->dir.'/backups'; - } - - $this->validateFormatClass(); - } - - /** - * format - * - * kind of a quick fix since we are using static methods, - * currently need to instantiate teh class to check instanceof why?? - * - * Checks the format of the database being accessed - */ - protected function validateFormatClass() - { - if (!class_exists($this->format)) - { - throw new \Exception('Filebase Error: Missing format class in config.'); - } - - // instantiate the format class - $format_class = new $this->format; - - // check now if that class is part of our interface - if (!$format_class instanceof \Filebase\Format\FormatInterface) - { - throw new \Exception('Filebase Error: Format Class must be an instance of Filebase\Format\FormatInterface'); - } - } -} diff --git a/admin/db/vendor/tmarois/filebase/src/Database.php b/admin/db/vendor/tmarois/filebase/src/Database.php deleted file mode 100644 index 40b189b..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Database.php +++ /dev/null @@ -1,432 +0,0 @@ -getVersion() - */ - const VERSION = '1.0.24'; - - /** - * $config - * - * Stores all the configuration object settings - * \Filebase\Config - */ - protected $config; - - /** - * Database constructor. - * - * @param array $config - * - * @throws FilesystemException - */ - public function __construct(array $config = []) - { - $this->config = new Config($config); - - // if we are set to read only, don't care to look at the directory. - if ($this->config->read_only === true) return false; - - // Check directory and create it if it doesn't exist - if (!is_dir($this->config->dir)) - { - if (!@mkdir($this->config->dir, 0777, true)) - { - throw new FilesystemException(sprintf('`%s` doesn\'t exist and can\'t be created.', $this->config->dir)); - } - } - else if (!is_writable($this->config->dir)) - { - throw new FilesystemException(sprintf('`%s` is not writable.', $this->config->dir)); - } - } - - /** - * version - * - * gets the Filebase version - * - * @return VERSION - */ - public function version() - { - return self::VERSION; - } - - /** - * findAll() - * - * Finds all documents in database directory. - * Then returns you a list of those documents. - * - * @param bool $include_documents (include all document objects in array) - * @param bool $data_only (if true only return the documents data not the full object) - * - * @return array $items - */ - public function findAll($include_documents = true, $data_only = false) - { - $format = $this->config->format; - - $file_extension = $format::getFileExtension(); - $file_location = $this->config->dir.'/'; - - $all_items = Filesystem::getAllFiles($file_location, $file_extension); - if (!$include_documents) - { - return $all_items; - } - $items = []; - - foreach($all_items as $a) - { - if ($data_only === true) - { - $items[] = $this->get($a)->getData(); - } - else - { - $items[] = $this->get($a); - } - } - - return $items; - } - - /** - * get - * - * retrieves a single result (file) - * - * @param mixed $id - * - * @return $document \Filebase\Document object - */ - public function get($id) - { - $content = $this->read($id); - - $document = new Document($this); - $document->setId($id); - - if ($content) - { - if (isset($content['__created_at'])) $document->setCreatedAt($content['__created_at']); - if (isset($content['__updated_at'])) $document->setUpdatedAt($content['__updated_at']); - - $this->set($document,(isset($content['data']) ? $content['data'] : [])); - } - - return $document; - } - - /** - * has - * - * Check if a record already exists - * - * @param mixed $id - * - * @return bool true/false - */ - public function has($id) - { - $format = $this->config->format; - $record = Filesystem::read( $this->config->dir.'/'.Filesystem::validateName($id, $this->config->safe_filename).'.'.$format::getFileExtension() ); - - return $record ? true : false; - } - - /** - * backup - * - * @param string $location (optional) - * - * @return $document \Filebase\Backup object - */ - public function backup($location = '') - { - if ($location) - { - return new Backup($location, $this); - } - - return new Backup($this->config->backupLocation, $this); - } - - /** - * set - * - * @param $document \Filebase\Document object - * @param mixed $data should be an array - * - * @return $document \Filebase\Document object - */ - public function set(Document $document, $data) - { - if ($data) - { - foreach($data as $key => $value) - { - if (is_array($value)) $value = (array) $value; - $document->{$key} = $value; - } - } - - return $document; - } - - /** - * count - * - * - * @return int $total - */ - public function count() - { - return count($this->findAll(false)); - } - - /** - * @param Document $document - * @param string $wdata - * @return bool|Document - * @throws SavingException - */ - public function save(Document $document, $wdata = '') - { - if ($this->config->read_only === true) - { - throw new SavingException("This database is set to be read-only. No modifications can be made."); - } - - $format = $this->config->format; - $id = $document->getId(); - $file_extension = $format::getFileExtension(); - $file_location = $this->config->dir.'/'.Filesystem::validateName($id, $this->config->safe_filename).'.'.$file_extension; - $created = $document->createdAt(false); - - if (isset($wdata) && $wdata !== '') - { - $document = new Document( $this ); - $document->setId($id); - $document->set($wdata); - $document->setCreatedAt($created); - } - - if (!Filesystem::read($file_location) || $created==false) - { - $document->setCreatedAt(time()); - } - - $document->setUpdatedAt(time()); - - try { - $data = $format::encode( $document->saveAs(), $this->config->pretty ); - } catch (EncodingException $e) { - // TODO: add logging - throw new SavingException("Can not encode document.", 0, $e); - } - - if (Filesystem::write($file_location, $data)) - { - $this->flushCache(); - - return $document; - } - - return false; - } - - /** - * query - * - * - */ - public function query() - { - return new Query($this); - } - - /** - * Read and return Document from filesystem by name. - * If doesn't exists return new empty Document. - * - * @param $name - * - * @throws Exception|ReadingException - * @return array|null - */ - protected function read($name) - { - $format = $this->config->format; - - $file = Filesystem::read( - $this->config->dir . '/' - . Filesystem::validateName($name, $this->config->safe_filename) - . '.' . $format::getFileExtension() - ); - - if ($file !== false) { - return $format::decode($file); - } - - return null; - } - - /** - * delete - * - * @param $document \Filebase\Document object - * @return (bool) true/false if file was deleted - */ - public function delete(Document $document) - { - if ($this->config->read_only === true) - { - throw new Exception("This database is set to be read-only. No modifications can be made."); - } - - $format = $this->config->format; - - $d = Filesystem::delete($this->config->dir.'/'.Filesystem::validateName($document->getId(), $this->config->safe_filename).'.'.$format::getFileExtension()); - - $this->flushCache(); - - return $d; - } - - /** - * truncate - * - * Alias for flush(true) - * - * @return @see flush - */ - public function truncate() - { - return $this->flush(true); - } - - /** - * flush - * - * This will DELETE all the documents within the database - * - * @param bool $confirm (confirmation before proceeding) - * @return void - */ - public function flush($confirm = false) - { - if ($this->config->read_only === true) - { - throw new Exception("This database is set to be read-only. No modifications can be made."); - } - - if ($confirm!==true) - { - throw new Exception("Database Flush failed. You must send in TRUE to confirm action."); - } - - $format = $this->config->format; - $documents = $this->findAll(false); - foreach($documents as $document) - { - Filesystem::delete($this->config->dir.'/'.$document.'.'.$format::getFileExtension()); - } - - if ($this->count() === 0) - { - return true; - } - - throw new Exception("Could not delete all database files in ".$this->config->dir); - } - - /** - * flushCache - * - * - */ - public function flushCache() - { - if ($this->getConfig()->cache===true) - { - $cache = new Cache($this); - $cache->flush(); - } - } - - /** - * toArray - * - * @param \Filebase\Document - * @return array - */ - public function toArray(Document $document) - { - return $this->objectToArray( $document->getData() ); - } - - /** - * objectToArray - * - */ - public function objectToArray($obj) - { - if (!is_object($obj) && !is_array($obj)) - { - return $obj; - } - - $arr = []; - foreach ($obj as $key => $value) - { - $arr[$key] = $this->objectToArray($value); - } - - return $arr; - } - - /** - * getConfig - * - * @return $config - */ - public function getConfig() - { - return $this->config; - } - - /** - * __call - * - * Magic method to give us access to query methods on db class - * - */ - public function __call($method,$args) - { - if(method_exists($this,$method)) { - return $this->$method(...$args); - } - - if(method_exists(Query::class,$method)) { - return (new Query($this))->$method(...$args); - } - - throw new \BadMethodCallException("method {$method} not found on 'Database::class' and 'Query::class'"); - } - -} diff --git a/admin/db/vendor/tmarois/filebase/src/Document.php b/admin/db/vendor/tmarois/filebase/src/Document.php deleted file mode 100644 index 0fcf27d..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Document.php +++ /dev/null @@ -1,393 +0,0 @@ -__database = $database; - } - - /** - * saveAs - * - */ - public function saveAs() - { - $data = (object) []; - $vars = get_object_vars($this); - - foreach($vars as $k=>$v) - { - if (in_array($k,['__database','__id','__cache'])) continue; - $data->{$k} = $v; - } - - return $data; - } - - /** - * save() - * - * Saving the document to disk (file) - * - * @param mixed $data (optional, only if you want to "replace" entire doc data) - * @return @see \Filebase\Database save() - */ - public function save($data = '') - { - Validate::valid($this); - - return $this->__database->save($this, $data); - } - - /** - * delete - * - * Deletes document from disk (file) - * - * @return @see \Filebase\Database delete() - */ - public function delete() - { - return $this->__database->delete($this); - } - - /** - * set - * - */ - public function set($data) - { - return $this->__database->set($this, $data); - } - - /** - * toArray - * - */ - public function toArray() - { - return $this->__database->toArray($this); - } - - /** - * __set - * - */ - public function __set($name, $value) - { - $this->data[$name] = $value; - } - - /** - * __get - * - */ - public function &__get($name) - { - if (!array_key_exists($name, $this->data)) - { - $this->data[$name] = null; - } - - return $this->data[$name]; - } - - /** - * __isset - * - */ - public function __isset($name) - { - return isset($this->data[$name]); - } - - /** - * __unset - * - */ - public function __unset($name) - { - unset($this->data[$name]); - } - - - //-------------------------------------------------------------------- - - - /** - * filter - * - * Alias of customFilter - * - * @see customFilter - */ - public function filter($field = 'data', $paramOne = '', $paramTwo = '') - { - return $this->customFilter($field, $paramOne, $paramTwo); - } - - /** - * customFilter - * - * Allows you to run a custom function around each item - * - * @param string $field - * @param callable $function - * @return array $r items that the callable function returned - */ - public function customFilter($field = 'data', $paramOne = '', $paramTwo = '') - { - $items = $this->field($field); - - if (is_callable($paramOne)) - { - $function = $paramOne; - $param = $paramTwo; - } - else - { - if (is_callable($paramTwo)) - { - $function = $paramTwo; - $param = $paramOne; - } - } - - - if (!is_array($items) || empty($items)) - { - return []; - } - - $r = []; - foreach($items as $index => $item) - { - $i = $function($item, $param); - - if ($i!==false && !is_null($i)) - { - $r[$index] = $i; - } - } - - $r = array_values($r); - - return $r; - - } - - /** - * getDatabase - * - * @return $database - */ - public function getDatabase() - { - return $this->__database; - } - - /** - * getId - * - * @return mixed $__id - */ - public function getId() - { - return $this->__id; - } - - /** - * getData - * - * @return mixed data - */ - public function getData() - { - return $this->data; - } - - /** - * setId - * - * @param mixed $id - */ - public function setId($id) - { - $this->__id = $id; - - return $this; - } - - /** - * setCache - * - * @param boolean $cache - */ - public function setFromCache($cache = true) - { - $this->__cache = $cache; - - return $this; - } - - /** - * isCache - * - */ - public function isCache() - { - return $this->__cache; - } - - /** - * createdAt - * - * When this document was created (or complete replaced) - * - * @param string $format php date format (default Y-m-d H:i:s) - * @return string date format - */ - public function createdAt($format = 'Y-m-d H:i:s') - { - if (!$this->__created_at) - { - return date($format); - } - - if ($format !== false) - { - return date($format, $this->__created_at); - } - - return $this->__created_at; - } - - /** - * updatedAt - * - * When this document was updated - * - * @param string $format php date format (default Y-m-d H:i:s) - * @return string date format - */ - public function updatedAt($format = 'Y-m-d H:i:s') - { - if (!$this->__updated_at) - { - return date($format); - } - - if ($format !== false) - { - return date($format, $this->__updated_at); - } - - return $this->__updated_at; - } - - /** - * setCreatedAt - * - * @param int $created_at php time() - */ - public function setCreatedAt($created_at) - { - $this->__created_at = $created_at; - - return $this; - } - - /** - * setuUpdatedAt - * - * @param int $updated_at php time() - */ - public function setUpdatedAt($updated_at) - { - $this->__updated_at = $updated_at; - - return $this; - } - - /** - * field - * - * Gets property based on a string - * - * You can also use string separated by dots for nested arrays - * key_1.key_2.key_3 etc - * - * @param string $field - * @return string $context property - */ - public function field($field) - { - $parts = explode('.', $field); - $context = $this->data; - - if ($field=='data') { - return $context; - } - - if ($field == '__created_at') { - return $this->__created_at; - } - - if ($field == '__updated_at') { - return $this->__updated_at; - } - - if ($field == '__id') { - return $this->__id; - } - - foreach($parts as $part) - { - if (trim($part) == '') - { - return false; - } - - if (is_object($context)) - { - if(!property_exists($context, $part)) - { - return false; - } - - $context = $context->{$part}; - } - else if (is_array($context)) - { - if(!array_key_exists($part, $context)) - { - return false; - } - - $context = $context[$part]; - } - } - - return $context; - } - -} diff --git a/admin/db/vendor/tmarois/filebase/src/Filesystem.php b/admin/db/vendor/tmarois/filebase/src/Filesystem.php deleted file mode 100644 index 307358d..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Filesystem.php +++ /dev/null @@ -1,114 +0,0 @@ -inputData = $inputData; - } - - public function getInputData() - { - return $this->inputData; - } -} - diff --git a/admin/db/vendor/tmarois/filebase/src/Format/FormatInterface.php b/admin/db/vendor/tmarois/filebase/src/Format/FormatInterface.php deleted file mode 100644 index cbbcf64..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Format/FormatInterface.php +++ /dev/null @@ -1,8 +0,0 @@ -'; - } - - /** - * @param $data - * @return mixed - * @throws FormatException - */ - public static function decode($data) - { - $decoded = substr($data, 41); - $decoded = substr($decoded, 0, -4); - $decoded = json_decode($decoded, true); - - if ($data !== false && $decoded === null) { - throw new DecodingException( - "json_decode: '" . json_last_error_msg() . "'", - 0, - null, - $data - ); - } - - return $decoded; - } -} diff --git a/admin/db/vendor/tmarois/filebase/src/Format/Json.php b/admin/db/vendor/tmarois/filebase/src/Format/Json.php deleted file mode 100644 index 6642208..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Format/Json.php +++ /dev/null @@ -1,60 +0,0 @@ -', - '<', - '>=', - '<=', - 'IN', - 'NOT', - 'LIKE', - 'NOT LIKE', - 'REGEX' - ]; - - - /** - * $predicates - * - * Query clauses - */ - protected $predicates = []; - - /** - * add - * - */ - public function add($logic,$arg) - { - if (!is_array($arg)) - { - throw new \InvalidArgumentException('Predicate Error: argument passed must be type of array'); - } - - if (count($arg) == 1) - { - if (isset($arg[0]) && is_array($arg[0])) - { - foreach($arg[0] as $key => $value) - { - if ($value == '') continue; - - $arg = $this->formatWhere($key, $value); - } - } - } - - if (count($arg) != 3) - { - throw new \InvalidArgumentException('Predicate Error: Must have 3 arguments passed - '.count($arg).' given'); - } - - if (!in_array($arg[1], $this->allowed_operators)) - { - throw new \InvalidArgumentException('Predicate Error: Unknown Operator '.$arg[1]); - } - - $arg[0] = trim($arg[0]); - - if ($arg[0] == '') - { - throw new \InvalidArgumentException('Field name can not be empty'); - } - - $this->predicates[$logic][] = $arg; - } - - /** - * formatWhere - * - */ - protected function formatWhere($key, $value) - { - return [$key,'==',$value]; - } - - /** - * get - * - */ - public function get() - { - return array_filter($this->predicates); - } -} diff --git a/admin/db/vendor/tmarois/filebase/src/Query.php b/admin/db/vendor/tmarois/filebase/src/Query.php deleted file mode 100644 index 3712a29..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Query.php +++ /dev/null @@ -1,249 +0,0 @@ -select() - * - * Set the selected fields you wish to return from each document - * - */ - public function select($fields) - { - if (is_string($fields)) - { - $fields = explode(',',trim($fields)); - } - - if (is_array($fields)) - { - $this->fields = $fields; - } - - return $this; - } - - /** - * ->where() - * - */ - public function where(...$arg) - { - $this->addPredicate('and', $arg); - - return $this; - } - - /** - * ->andWhere() - * - */ - public function andWhere(...$arg) - { - $this->addPredicate('and', $arg); - - return $this; - } - - /** - * ->orWhere() - * - */ - public function orWhere(...$arg) - { - $this->addPredicate('or', $arg); - - return $this; - } - - /** - * ->limit() - * - */ - public function limit($limit, $offset = 0) - { - $this->limit = (int) $limit; - - if ($this->limit === 0) - { - $this->limit = 9999999; - } - - $this->offset = (int) $offset; - - return $this; - } - - /** - * ->orderBy() - * - */ - public function orderBy($field, $sort = 'ASC') - { - if (count($this->orderBy) == 1 && $this->orderBy[0] == '') { - // Just set the initial index - $this->orderBy[0] = $field; - $this->sortBy[0] = strtoupper($sort); - } else { - $this->orderBy[] = $field; - $this->sortBy[] = strtoupper($sort); - } - - return $this; - } - - /** - * addPredicate - * - */ - protected function addPredicate($logic,$arg) - { - $this->predicate->add($logic, $arg); - } - - /** - * ->getDocuments() - * - * - */ - public function getDocuments() - { - return $this->documents; - } - - /** - * ->results() - * - * @param bool $data_only - default:true (if true only return the documents data not the full object) - * - */ - public function results( $data_only = true ) - { - if ($data_only === true && empty($this->fields)) - { - return parent::run()->toArray(); - } - - return $this->resultDocuments(); - } - - /** - * ->resultDocuments() - * - */ - public function resultDocuments() - { - return parent::run()->getDocuments(); - } - - /** - * ->first() - * - * @param bool $data_only - default:true (if true only return the documents data not the full object) - * - */ - public function first( $data_only = true ) - { - if ($data_only === true && empty($this->fields)) - { - $results = parent::run()->toArray(); - return current($results); - } - - $results = parent::run()->getDocuments(); - return current($results); - } - - /** - * ->last() - * - * @param bool $data_only - default:true (if true only return the documents data not the full object) - * - */ - public function last( $data_only = true ) - { - if ($data_only === true && empty($this->fields)) - { - $results = parent::run()->toArray(); - return end($results); - } - - $results = parent::run()->getDocuments(); - return end($results); - } - - /** - * ->count() - * - * Count and return the number of documents in array - * - */ - public function count() - { - $results = parent::run()->getDocuments(); - return count($results); - } - - /** - * toArray - * - * @param \Filebase\Document - * @return array - */ - public function toArray() - { - $docs = []; - - if (!empty($this->documents)) - { - foreach($this->documents as $document) - { - $docs[] = (array) $document->getData(); - } - } - - return $docs; - } - - /** - * delete - * - * The ability to delete items using queries - * - * Delete by condition or delete all within clause - * - * @return void - */ - public function delete($input = null) - { - $items = $this->resultDocuments(); - $condition = $input; - foreach($items as $item) - { - if (is_object($input)) { - $condition = $input($item); - - if ($condition) { - $item->delete(); - } - } - else { - $item->delete(); - } - } - } -} diff --git a/admin/db/vendor/tmarois/filebase/src/QueryLogic.php b/admin/db/vendor/tmarois/filebase/src/QueryLogic.php deleted file mode 100644 index 0d97c4f..0000000 --- a/admin/db/vendor/tmarois/filebase/src/QueryLogic.php +++ /dev/null @@ -1,243 +0,0 @@ -database = $database; - $this->predicate = new Predicate(); - - if ($this->database->getConfig()->cache===true) - { - $this->cache = new Cache($this->database); - } - } - - /** - * loadDocuments - * - */ - private function loadDocuments() - { - $predicates = $this->predicate->get(); - - if ($this->cache===false) - { - $this->documents = $this->database->findAll(true,false); - return $this; - } - - $this->cache->setKey(json_encode($predicates)); - - if ($cached_documents = $this->cache->get()) - { - $this->documents = $cached_documents; - - $this->sort(); - $this->offsetLimit(); - return $this; - } - $this->documents = $this->database->findAll(true,false); - return $this; - } - - /** - * run - * - */ - public function run() - { - $predicates = $this->predicate->get(); - $this->documents = []; - $cached_documents = false; - - if (empty($predicates)) - { - $predicates = 'findAll'; - } - - $this->loadDocuments(); - - if ($predicates !== 'findAll') - { - $this->documents = $this->filter($this->documents, $predicates); - } - - if ($this->cache !== false) - { - if ($cached_documents === false) - { - $dsave = []; - foreach($this->documents as $document) - { - $dsave[] = $document->getId(); - } - - $this->cache->store($dsave); - } - } - - $this->sort(); - $this->offsetLimit(); - - if (is_array($this->fields) && !empty($this->fields)) - { - foreach($this->documents as $index => $document) - { - $fields = []; - foreach($this->fields as $fieldTarget) - { - $fields[ $fieldTarget ] = $document->field($fieldTarget); - } - - $this->documents[$index] = $fields; - } - } - - return $this; - } - - /** - * filter - * - */ - protected function filter($documents, $predicates) - { - $results = []; - - $org_docs = $documents; - - if (isset($predicates['and']) && !empty($predicates['and'])) - { - foreach($predicates['and'] as $predicate) - { - list($field, $operator, $value) = $predicate; - - $documents = array_values(array_filter($documents, function ($document) use ($field, $operator, $value) { - return $this->match($document, $field, $operator, $value); - })); - - $results = $documents; - } - } - - if (isset($predicates['or']) && !empty($predicates['or'])) - { - foreach($predicates['or'] as $predicate) - { - list($field, $operator, $value) = $predicate; - - $documents = array_values(array_filter($org_docs, function ($document) use ($field, $operator, $value) { - return $this->match($document, $field, $operator, $value); - })); - - $results = array_unique(array_merge($results, $documents), SORT_REGULAR); - } - } - - return $results; - } - - /** - * offsetLimit - * - */ - protected function offsetLimit() - { - if ($this->limit != 0 || $this->offset != 0) - { - $this->documents = array_slice($this->documents, $this->offset, $this->limit); - } - } - - /** - * sort - * - */ - protected function sort() - { - if ($this->orderBy[0] == '') - { - return false; - } - - $sortlogic = new SortLogic($this->orderBy, $this->sortBy, 0); - usort($this->documents, [$sortlogic, 'sort']); - } - - /** - * match - * - */ - public function match($document, $field, $operator, $value) - { - $d_value = $document->field($field); - - switch (true) - { - case ($operator === '=' && $d_value == $value): - return true; - case ($operator === '==' && $d_value == $value): - return true; - case ($operator === '===' && $d_value === $value): - return true; - case ($operator === '!=' && $d_value != $value): - return true; - case ($operator === '!==' && $d_value !== $value): - return true; - case (strtoupper($operator) === 'NOT' && $d_value != $value): - return true; - case ($operator === '>' && $d_value > $value): - return true; - case ($operator === '>=' && $d_value >= $value): - return true; - case ($operator === '<' && $d_value < $value): - return true; - case ($operator === '<=' && $d_value <= $value): - return true; - case (strtoupper($operator) === 'LIKE' && preg_match('/'.$value.'/is',$d_value)): - return true; - case (strtoupper($operator) === 'NOT LIKE' && !preg_match('/'.$value.'/is',$d_value)): - return true; - case (strtoupper($operator) === 'IN' && in_array($d_value, (array) $value)): - return true; - case (strtoupper($operator) === 'IN' && in_array($value, (array) $d_value)): - return true; - case (strtoupper($operator) === 'REGEX' && preg_match($value, $d_value)): - return true; - default: - return false; - } - - } - -} diff --git a/admin/db/vendor/tmarois/filebase/src/SortLogic.php b/admin/db/vendor/tmarois/filebase/src/SortLogic.php deleted file mode 100644 index ef417e3..0000000 --- a/admin/db/vendor/tmarois/filebase/src/SortLogic.php +++ /dev/null @@ -1,79 +0,0 @@ -orderBy = $orderBy; - $this->sortDirection = $sortDirection; - $this->index = $index; - } - - /** - * Sorting callback - * - * @param Document $docA - * @param Document $docB - * @return return int (-1, 0, 1) - */ - public function sort($docA, $docB) - { - $propA = $docA->field($this->orderBy[$this->index]); - $propB = $docB->field($this->orderBy[$this->index]); - - if (strnatcasecmp($propA, $propB) == 0) - { - if (!isset($this->orderBy[$this->index + 1])) - { - return 0; - } - - // If they match and there are multiple orderBys, go deeper (recurse) - $sortlogic = new self($this->orderBy, $this->sortDirection, $this->index + 1); - return $sortlogic->sort($docA, $docB); - } - - if ($this->sortDirection[$this->index] == 'DESC') - { - return strnatcasecmp($propB, $propA); - } - else - { - return strnatcasecmp($propA, $propB); - } - } -} diff --git a/admin/db/vendor/tmarois/filebase/src/Validate.php b/admin/db/vendor/tmarois/filebase/src/Validate.php deleted file mode 100644 index 5d29ae9..0000000 --- a/admin/db/vendor/tmarois/filebase/src/Validate.php +++ /dev/null @@ -1,139 +0,0 @@ -toArray(); - - self::validateLoop($document,$object,self::getValidateRules($object)); - - return true; - } - - /** - * getValidateRules - * - * @param \Filebase\Document - * @return database->config - */ - public static function getValidateRules(Document $object) - { - return $object->getDatabase()->getConfig()->validate; - } - - /** - * validateLoop - * - * Loops over the document and finds invaild data - * Throws an exception if found, otherwise returns nothing - * - * @param array (of document data) - * @return vold - */ - protected static function validateLoop($document,$object,$rules) - { - foreach($rules as $key => $rule) - { - if ( (!isset($rule['valid.type']) ) && isset($document[$key])) - { - self::validateLoop($document[$key],$object,$rules[$key]); - - continue; - } - - self::validateRules($document,$key,$rules[$key],$object); - } - } - - /** - * validateRules - * - * Checks "valid.type" - * Checks "valid.requred" - * - * Throws exception error if matches are not met. - * - * @return \Filebase\Document Object - */ - protected static function validateRules($document,$key,$rules,$object) - { - // checks variable type - if (isset($document[$key],$rules['valid.type'])) - { - if (!in_array($rules['valid.type'],['string','str','int','integer','arr','array'])) - { - throw new \Exception('Validation Failed: Invaild Property Type "'.$rules['valid.type'].'"'); - } - - if (!self::checkType($document[$key],$rules['valid.type'])) - { - throw new \Exception('Validation Failed setting variable on '.$object->getId().' - ['.$key.'] does not match type "'.$rules['valid.type'].'"'); - } - } - - // check if variable is required - if (isset($rules['valid.required']) && $rules['valid.required']===true) - { - if (!isset($document[$key])) - { - throw new \Exception('Validation Failed setting variable on '.$object->getId().' - ['.$key.'] is required'); - } - } - - return $object; - } - - /** - * checkType - * - * Checks type of variable and sees if it matches - * - * @return boolean (true or false) - */ - protected static function checkType($variable, $type) - { - switch($type) - { - case 'string': - case 'str': - if (is_string($variable)) - { - return true; - } - - break; - - case 'integer': - case 'int': - if (is_integer($variable)) - { - return true; - } - - break; - - case 'array': - case 'arr': - if (is_array($variable)) - { - return true; - } - - break; - - default: - return false; - } - - return false; - } -} diff --git a/admin/languages/de_DE.json b/admin/languages/de_DE.json deleted file mode 100644 index cfe7943..0000000 --- a/admin/languages/de_DE.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "Short links manager": "Short Links Manager", - "Create your own link shortener": "Erstelle deinen eigenen Link-Shortener", - "Home page": "Startseite", - "Dashboard": "Instrumententafel", - "Users": "Benutzer", - "Settings": "Einstellungen", - "About": "Informationen", - "Add new": "Neue hinzufügen", - "Total clicks": "Gesamtanzahl an Klicks", - "Background image": "Hintergrundbild", - "Logo font": "Logo Schriftart", - "Access to the site requires administrator privileges": "Der Zugriff auf die Site erfordert Administratorrechte", - "Top referrer": "Top Referrero", - "Top language": "Spitzensprache", - "total links": "Links insgesamt", - "English": "Englisch", - "Email, SMS, Direct": "Email, SMS, Direkt", - "Save settings": "Einstellungen speichern", - "Main": "Main", - "Redirects": "Umleiten", - "Cache": "Zwischenspeicher", - "Encryption": "Verschlüsselung", - "Analytics": "Analytics", - "Captcha": "Captcha", - "Languages": "Sprachen", - "Miscellaneous": "Sonstiges", - "URLs": "URLs", - "Main website URL": "URL der Hauptwebsite", - "Dashboard URL": "Adres kokpitu", - "Attention":"Beachtung", - "Change URLs only if you have moved the site to a different domain or folder. Otherwise, access to the panel may be blocked.": "Ändern Sie URLs nur, wenn Sie die Site in eine andere Domäne oder einen anderen Ordner verschoben haben. Andernfalls kann der Zugang zum Panel blockiert werden.", - "Redirect 404 page": "404-Seite umleiten", - "URL to which redirect error 404": "URL, zu der Fehler 404 umgeleitet wird", - "Redirect Home page": "Startseite umleiten", - "URL to which redirect home page": "URL, zu der die Startseite umgeleitet wird", - "Enable Cache for records database": "Cache für Datensatzdatenbank aktivieren", - "ReCaptcha site key": "Site-Schlüssel ReCaptcha", - "ReCaptcha secret key": "geheimer Schlüssel ReCaptcha", - "You can enable %s for admin panel login.": "Sie können %s für die Anmeldung im Admin-Bereich aktivieren.", - "Leave these fields blank if you want to disable ReCaptcha V3": "Lassen Sie diese Felder leer, wenn Sie ReCaptcha V3 deaktivieren möchten", - "Connection encryption": "Verbindungsverschlüsselung", - "Force SSL connection for redirects": "SSL-Verbindung für Umleitungen erzwingen", - "Force SSL connection for dashboard": "SSL-Verbindung für Dashboard erzwingen", - "You don't have to spend money on certificates from companies like Comodo or RapidSSL.": "Sie müssen kein Geld für Zertifikate von Unternehmen wie Comodo oder RapidSSL ausgeben.", - "You can generate a free certificate with": "Mit können Sie ein kostenloses Zertifikat erstellen", - "SSL is recommended.": "SSL wird empfohlen.y", - "You protect both yourself and your users against a number of attacks. MIDM and Session Hijacking are one of the most dangerous. Never put safety second.": "Sie schützen sich und Ihre Benutzer vor einer Reihe von Angriffen. MIDM und Session Hijacking gehören zu den gefährlichsten. Niemals die Sicherheit an zweiter Stelle setzen.", - "JS Redirection": "JS-Umleitung", - "Tracking Code (gtag)": "Tracking-Code (gtag)", - "Redirect after:": "Weiterleiten nach:", - "Immediately": "Sofort", - "second": "zweite", - "seconds": "sekunden", - "eg.:": "z.B.:", - "JavaScript redirection and the use of Google Analytics may not work. This method is less effective and is not recommended.": "Die JavaScript-Umleitung und die Verwendung von Google Analytics funktionieren möglicherweise nicht. Diese Methode ist weniger effektiv und wird nicht empfohlen.", - "Anyway, if you want you can use it.": "Wie auch immer, wenn Sie möchten, können Sie es verwenden.", - "Choose how languages are detected": "Wählen Sie aus, wie Sprachen erkannt werden", - "Automatically (browser)": "Automatisch (Browser)", - "Automatically (geolocation)": "Automatisch (Geolocation)", - "Permanently defined": "Fest definiert", - "Statically defined language": "Statisch definierte Sprache", - "You can change these options only in the %s file": "Sie können diese Optionen nur in der %s-Datei ändern", - "Dashboard path for URL": "Dashboard-Pfad für URL", - "Users database": "Benutzerdatenbank", - "Options database": "Optionsdatenbank", - "Records database": "Datensatzdatenbank", - "Cryptographic method for passwords": "Kryptografische Methode für Kennwörter", - "Changing the cryptographic method will make all passwords stop working.": "Wenn Sie die Verschlüsselungsmethode ändern, funktionieren alle Kennwörter nicht mehr.", - "Debugging": "Debuggen", - "Remember to turn off debugging if you have stopped testing the page.": "Denken Sie daran, das Debuggen zu deaktivieren, wenn Sie das Testen der Seite beendet haben.", - "Enabled": "Aktiviert", - "Disabled": "Deaktiviert", - "Edit": "Bearbeiten", - "Delete": "Löschen", - "Never": "Noch nie", - "Unknown": "Unbekannt", - "Add new user": "Neuen Benutzer hinzufügen", - "Date created": "Datum erstellt", - "Last login": "Letzte Anmeldung", - "Administrator": "Administrator", - "Analyst": "Analytiker", - "Manager": "Manager", - "Has full permissions to do everything.": "Hat die vollen Berechtigungen, um alles zu tun.", - "Can add and delete records. Cannot change settings or add users.": "Kann Datensätze hinzufügen und löschen. Einstellungen können nicht geändert oder Benutzer hinzugefügt werden.", - "Can only view data.": "Kann nur Daten anzeigen.", - "Something went wrong!": "Etwas ist schief gelaufen!", - "Success!": "Erfolg!", - "Settings have been saved.": "Einstellungen wurden gespeichert.", - "Holy guacamole!": "Error!", - "New link was added.": "Neuer Link wurde hinzugefügt.", - "You must provide a URL!": "Sie müssen eine URL angeben!", - "The URL you entered is not valid!": "Die eingegebene URL ist ungültig!", - "The 404 page redirect URL is not valid!": "Die Weiterleitungs-URL für 404-Seiten ist ungültig!", - "The home page redirect URL is not valid!": "Die Weiterleitungs-URL der Homepage ist ungültig!" -} \ No newline at end of file diff --git a/admin/languages/pl_PL.json b/admin/languages/pl_PL.json deleted file mode 100644 index 0548757..0000000 --- a/admin/languages/pl_PL.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "Short links manager": "Manadżer linków", - "Create your own link shortener": "Stwórz swój własny skracacz adresów", - "Home page": "Strona główna", - "Dashboard": "Kokpit", - "Users": "Użytkownicy", - "Settings": "Ustawienia", - "About": "Informacje", - "Add new": "Dodaj nowy", - "Total clicks": "Kliknięcia", - "Background image": "Obrazek w tle", - "Logo font": "Krój pisma logo", - "Access to the site requires administrator privileges": "Dostęp do witryny wymaga uprawnień administratora", - "Top referrer": "Najpopularniejsze źródło", - "Top language": "Najpopularniejszy język", - "total links": "wszystkich odnośników", - "English": "Angielski", - "Poland": "Polska", - "Email, SMS, Direct": "Email, SMS, Bezpośredni", - "Save settings": "Zapisz ustawienia", - "Main": "Główne", - "Redirects": "Przekierowania", - "Cache": "Pamięć podręczna", - "Encryption": "Szyfrowanie", - "Analytics": "Analityka", - "Captcha": "Captcha", - "Languages": "Języki", - "Miscellaneous": "Dodatkowe", - "URLs": "Adresy", - "Main website URL": "Główny adres witryny", - "Dashboard URL": "Adres kokpitu", - "Attention":"Uwaga", - "Change URLs only if you have moved the site to a different domain or folder. Otherwise, access to the panel may be blocked.": "Zmień adresy tylko jeśli witryna została przeniesiona do innego folderu lub domeny. W innym przypadku, dostęp do panelu może zostać zablokowany.", - "Redirect 404 page": "Przekieruj stronę 404", - "URL to which redirect error 404": "Adres, na który przekierować błąd 404", - "Redirect Home page": "Przekieruj stronę główną", - "URL to which redirect home page": "Adres, na który przekierować stronę główną", - "Enable Cache for records database": "Włącz pamięć podręczną dla bazy danych rekordów", - "ReCaptcha site key": "Klucz strony ReCaptcha", - "ReCaptcha secret key": "Sekretny klucz ReCaptcha", - "You can enable %s for admin panel login.": "Możesz włączyć %s dla logowania do kokpitu.", - "Leave these fields blank if you want to disable ReCaptcha V3": "Pozostaw te pola puste, jeśli chcesz wyłączyć ReCaptcha V3", - "Connection encryption": "Szyfrowanie połączenia", - "Force SSL connection for redirects": "Wymuś połączenie SSL dla przekierowań", - "Force SSL connection for dashboard": "Wymuś połączenie SSL dla kokpitu", - "You don't have to spend money on certificates from companies like Comodo or RapidSSL.": "Nie musisz wydawać pieniędzy na certyfikaty od firm jak Comodo albo RapidSSL", - "You can generate a free certificate with": "Możesz wygenerować certyfikat za darmo z pomocą", - "SSL is recommended.": "SSL jest rekomendowany", - "You protect both yourself and your users against a number of attacks. MIDM and Session Hijacking are one of the most dangerous. Never put safety second.": "Chronisz siebie i swoich użytkowników przed atakami. MIDM i Hijacking sesji są jednymi z bardziej groźnych. Nigdy nie stawiaj bezpieczeństwa na drugim miejscu", - "JS Redirection": "Przekierowanie przez JS", - "Tracking Code (gtag)": "Kod śledzenia (gtag)", - "Redirect after:": "Przekieruj po:", - "Immediately": "Natychmiast", - "second": "sekundzie", - "seconds": "sekundach", - "eg.:": "np:", - "JavaScript redirection and the use of Google Analytics may not work. This method is less effective and is not recommended.": "Przekierowanie przez JavaScript i używanie Google Analytics może nie działać. Ta metoda jest mniej efektywna i nie jest rekomendowana.", - "Anyway, if you want you can use it.": "Bądź co bądź, jeśli chcesz, możesz jej użyć", - "Choose how languages are detected": "Wybierz jak język ma być wykrywany", - "Automatically (browser)": "Automatycznie (przeglądarka)", - "Automatically (geolocation)": "Automatycznie (geolokalizacja)", - "Permanently defined": "Zdefiniowany na stałe", - "Statically defined language": "Statycznie wybrany język", - "You can change these options only in the %s file": "Możesz zmienić te ustawienia tylko w pliku %s", - "Dashboard path for URL": "Ścieżka dla URL kokpitu", - "Users database": "Baza danych użytkowników", - "Options database": "Baza danych opcji", - "Records database": "Baza danych rekordów", - "Cryptographic method for passwords": "Metoda kryptograficzna dla haseł", - "Changing the cryptographic method will make all passwords stop working.": "Zmiana metody kryptograficznej sprawi, że wszystkie hasła przestaną działać.", - "Debugging": "Debugowanie", - "Remember to turn off debugging if you have stopped testing the page.": "Pamiętaj, aby wyłączyć debugowanie kiedy skończysz testować witrynę.", - "Enabled": "Włączone", - "Disabled": "Wyłączone", - "Edit": "Edytuj", - "Delete": "Usuń", - "Never": "Nigdy", - "Close": "Zamknij", - "Unknown": "Nieznany", - "Add user": "Dodaj użytkownika", - "Delete user": "Usuń użytkownika", - "Add new user": "Dodaj nowego użytkownika", - "Date created": "Data utworzenia", - "Last login": "Ostatnie logowanie", - "Administrator": "Administrator", - "Analyst": "Analityk", - "Manager": "Menedżer", - "Has full permissions to do everything.": "Ma pełne uprawnienia, aby robić wszystko", - "Can add and delete records. Cannot change settings or add users.": "Może dodawać i usuwać rekordy. Nie może zmieniać ustawień ani dodawać użytkowników", - "Can only view data.": "Może tylko przeglądać dane", - "Something went wrong!": "Coś poszło nie tak!", - "Success!": "Sukces!", - "Settings have been saved.": "Ustawienia zostały zapisane", - "Holy guacamole!": "Motyla noga!", - "New link was added.": "Nowy link został dodany.", - "You must provide a URL!": "Musisz podać adres URL", - "The URL you entered is not valid!": "Podany adres jest nieprawidłowy!", - "The 404 page redirect URL is not valid!": "Adres przekierowania strony 404 jest nieprawidłowy!", - "The home page redirect URL is not valid!": "Adres przekierowania strony głównej jest nieprawidłowy!", - "A new user has been added.": "Nowy użytkownik został dodany.", - "Login, password and email fields are required!": "Pola login, hasło i email są wymagane!", - "Login field contains illegal characters!": "Nazwa użytkownika zawiera niedozwole znaki!", - "Email field is invalid!": "Pole e-mail jest nieprawidłowe!", - "Password is too short!": "Hasło jest za krótkie!", - "The password is too simple. Use letters, numbers and special characters!": "Hasło jest za proste. Użyj liter, cyfr oraz znaków specjalnych!", - "Passwords do not match!": "Hasła nie są jednakowe!", - "User with this login already exists!": "Użytkownik o tym loginie już istnieje!", - "Delete record": "Usuń rekord", - "Cancel": "Anuluj", - "Are you sure you want to delete the %s record?": "Na pewno chcesz usunąć rekord %s?", - "Username": "Nazwa użytkownika", - "Email address": "Adres email", - "Link has been copied to your clipboard": "Link został skopiowany do Twojego schowka", - "A record with this ID already exists!": "Rekord z podanym ID już istnieje!", - "Role": "Rola", - "Referrers": "Referenci", - "Locations": "Lokalizacje", - "Password": "Hasło", - "Confirm password": "Potwierdź hasło", - "This should not happen": "To nie powinno mieć miejsca", - "You will be redirected in a few seconds...": "Zostaniesz przekierowany za kilka sekund..." -} \ No newline at end of file diff --git a/admin/red-admin.php b/admin/red-admin.php deleted file mode 100644 index af9cb3d..0000000 --- a/admin/red-admin.php +++ /dev/null @@ -1,151 +0,0 @@ - - * @version $Id: red-admin.php;RED_ADMIN,v beta 1.0 2019/10/27 - * @access public - */ - class RED_ADMIN - { - private $RED; - private $LOGGED_IN; - - /** - * init - * Returns the RED_ADMIN object without initializing the object - * - * @access public - * @return object RED_ADMIN - */ - public static function init(RED $RED) : RED_ADMIN - { - return new RED_ADMIN($RED); - } - - /** - * __construct - * Checks permissions and redirects to the website, or return ajax - * - * @access public - * @return void - */ - public function __construct(RED $RED) - { - $this->RED = $RED; - - $this->RED->DB['users'] = new \Filebase\Database([ - 'dir' => DB_PATH.DB_USERS, - 'backupLocation' => DB_PATH.DB_USERS.'/backup', - 'format' => \Filebase\Format\Jdb::class - ]); - - session_start(); - session_regenerate_id(); - - self::isLoggedIn(); - - if(defined('RED_PAGE_DASHBOARD')) - { - if(!$this->LOGGED_IN) - { - if(RED_PAGE_DASHBOARD == 'ajax') - { - if(!isset($_POST['action'])) - exit('e99'); - if(!$_POST['action'] == 'sign_in') - exit('e99'); - - self::ajax(); - } - - header("Location: " . $this->RED->DB['options']->get('dashboard')->value); - exit; - } - else - { - if(RED_PAGE_DASHBOARD == 'users' && $this->RED->is_admin()) - $this->RED->page(['page' => 'users', 'title' => 'Users']); - else if(RED_PAGE_DASHBOARD == 'settings' && $this->RED->is_admin()) - $this->RED->page(['page' => 'settings', 'title' => 'Settings']); - else if(RED_PAGE_DASHBOARD == 'about') - $this->RED->page(['page' => 'about', 'title' => 'About']); - else if(RED_PAGE_DASHBOARD == 'signout') - self::signout(); - else if(RED_PAGE_DASHBOARD == 'ajax') - self::ajax(); - else - $this->RED->page(['title' => 'Page not found']); - } - } - else - { - if(!$this->LOGGED_IN) - $this->RED->page(['page' => 'login', 'title' => 'Sign In']); - else - $this->RED->page(['page' => 'dashboard', 'title' => 'Dashboard']); - } - } - - /** - * ajax - * Returns a new instance of the RED_AJAX class - * - * @access private - * @return object RED_AJAX - */ - private function ajax() : RED_AJAX - { - $this->RED->include(ADMPATH.'red-ajax.php'); - return RED_AJAX::init($this->RED); - } - - /** - * isLoggedIn - * Checks whether the user is logged in correctly - * - * @access public - * @return void - */ - public function isLoggedIn() : void - { - $this->LOGGED_IN = FALSE; - - if(isset($_SESSION['l'], $_SESSION['u'], $_SESSION['t'], $_SESSION['r'])) - { - $user = $this->RED->DB['users']->get(filter_var($_SESSION['u'], FILTER_SANITIZE_STRING)); - - if($user->token != NULL) - if($user->token == $_SESSION['t']) - if($_SESSION['l']) - $this->LOGGED_IN = TRUE; - } - } - - /** - * signout - * Destroys session, logs off user - * - * @access public - * @return void - */ - public function signout() : void - { - session_destroy(); - header("Location: " . $this->RED->DB['options']->get('dashboard')->value); - exit(); - } - } -?> diff --git a/admin/red-ajax.php b/admin/red-ajax.php deleted file mode 100644 index 8795ea6..0000000 --- a/admin/red-ajax.php +++ /dev/null @@ -1,371 +0,0 @@ - - * @version $Id: red-ajax.php;RED_AJAX,v beta 1.0 2019/10/27 - * @access public - */ - class RED_AJAX - { - private $RED; - private $ACTION; - private $NONCE; - - /** ERROR CODES */ - private const ERROR_MISSING_ACTION = 'e01'; - private const ERROR_MISSING_NONCE = 'e02'; - private const ERROR_INVALID_NONCE = 'e03'; - private const ERROR_INVALID_ACTION = 'e04'; - private const ERROR_INSUFFICIENT_PERMISSIONS = 'e05'; - private const ERROR_MISSING_ARGUMENTS = 'e06'; - private const ERROR_EMPTY_ARGUMENTS = 'e07'; - private const ERROR_ENTRY_EXISTS = 'e08'; - private const ERROR_ENTRY_DONT_EXISTS = 'e09'; - private const ERROR_INVALID_URL = 'e10'; - private const ERROR_INVALID_PASSWORD = 'e11'; - private const ERROR_PASSWORDS_DONT_MATCH = 'e12'; - private const ERROR_PASSWORD_TOO_SHORT = 'e13'; - private const ERROR_PASSWORD_TOO_SIMPLE = 'e14'; - private const ERROR_INVALID_EMAIL = 'e15'; - private const ERROR_SPECIAL_CHARACTERS = 'e16'; - - private const CODE_SUCCESS = 's01'; - - /** - * init - * Returns the RED_AJAX object without initializing the object - * - * @access public - * @return object RED_AJAX - */ - public static function init(RED $RED) : RED_AJAX - { - return new RED_AJAX($RED); - } - - /** - * __construct - * Verifies the permissions, correctness of the ajax request and checks the existence of the method - * - * @access public - * @return void - */ - public function __construct(RED $RED) - { - $this->RED = $RED; - - if (!isset($_POST['action'])) - exit(self::ERROR_MISSING_ACTION); - else - $this->ACTION = filter_var($_POST['action'], FILTER_SANITIZE_STRING); - - if (!isset($_POST['nonce'])) - exit(self::ERROR_MISSING_NONCE); - else - $this->NONCE = filter_var($_POST['nonce'], FILTER_SANITIZE_STRING); - - if(!self::verifyNonce()) - exit(self::ERROR_INVALID_NONCE); - - if(!self::checkAction()) - exit(self::ERROR_INVALID_ACTION); - else - $this->{$this->ACTION}(); - - die; //Kill if something is wrong - } - - /** - * verifyNonce - * Validates requested nonce - * - * @access private - * @return bool true/false - */ - private function verifyNonce() : bool - { - if(isset($_POST['nonce'])) - if($this->RED->compare_crypt('ajax_'.$this->ACTION.'_nonce', $this->NONCE, 'nonce')) - return TRUE; - else - return FALSE; - else - return FALSE; - } - - /** - * checkAction - * Checks whether the given ajax request exists - * - * @access private - * @return bool true/false - */ - private function checkAction() : bool - { - if(method_exists($this,$this->ACTION)) - return TRUE; - else - return FALSE; - } - - /** - * checkPermission - * Checks whether the given ajax request exists - * - * @access private - * @param string $type ('manager' / 'administrator') - * @return bool true/false - */ - private function checkPermission(string $type) : void - { - if($type == 'manager') - if(!$this->RED->is_manager()) - exit(self::ERROR_INSUFFICIENT_PERMISSIONS); - else - if(!$this->RED->is_admin()) - exit(self::ERROR_INSUFFICIENT_PERMISSIONS); - } - - - /** - Ajax methods - */ - - - private function add_record() : void - { - self::checkPermission('manager'); - - if(!isset( - $_POST['forward-url'], - $_POST['forward-slug'], - $_POST['randValue'] - )) - exit(self::ERROR_MISSING_ARGUMENTS); - - if(empty($_POST['forward-url']) || empty($_POST['randValue'])) - exit(self::ERROR_EMPTY_ARGUMENTS); - - if(empty($_POST['forward-slug'])) - $slug = filter_var($_POST['randValue'], FILTER_SANITIZE_STRING); - else - $slug = filter_var($_POST['forward-slug'], FILTER_SANITIZE_STRING); - - $record = $this->RED->DB['records']->get(strtolower($slug)); - - if(isset($record->url)) - if(!empty($record->url)) - exit(self::ERROR_ENTRY_EXISTS); - - //if(!filter_var($_POST['forward-url'], FILTER_VALIDATE_URL)) - // exit(self::ERROR_INVALID_URL); - - $record->save(array('name' => $slug, 'url' => $_POST['forward-url'], 'clicks' => 0)); - exit(self::CODE_SUCCESS); - } - - private function remove_record() : void - { - self::checkPermission('manager'); - - if(!isset($_POST['record_id'])) - exit(self::ERROR_MISSING_ARGUMENTS); - - $record = $this->RED->DB['records']->get(strtolower(filter_var($_POST['record_id'])), FILTER_SANITIZE_STRING); - - if(empty($record->url)) - exit(self::ERROR_ENTRY_DONT_EXISTS); - - $record = $this->RED->DB['records']->delete($record); - exit(self::CODE_SUCCESS); - } - - private function sign_in() : void - { - if(!isset( - $_POST['login'], - $_POST['password'] - )) - exit(self::ERROR_MISSING_ARGUMENTS); - - if(empty($_POST['login']) || empty($_POST['password'])) - exit(self::ERROR_ENTRY_DONT_EXISTS); - - $user = $this->RED->DB['users']->get(filter_var($_POST['login'], FILTER_SANITIZE_STRING)); - - if(empty($user->password)) - { - $user = $this->RED->DB['users']->select('__id,password')->where(['email' => filter_var($_POST['login'], FILTER_SANITIZE_STRING)])->results(); - - if(!isset($user[0]['password'])) - exit('self::ERROR_ENTRY_DONT_EXISTS'); - - if(empty($user[0]['password'])) - exit(self::ERROR_ENTRY_DONT_EXISTS); - - $userPassword = $user[0]['password']; - $userName = $user[0]['__id']; - } - else - { - $userPassword = $user->password; - $userName = $user->getId(); - } - - if(!$this->RED->compare_crypt(filter_var($_POST['password'], FILTER_SANITIZE_STRING), $userPassword)) - exit(self::ERROR_ENTRY_DONT_EXISTS); - - session_regenerate_id(); - - $token = $this->RED->encrypt($this->RED->rand(20), 'token'); - - $user = $this->RED->DB['users']->get($userName); - $user->token = $token; - $user->lastlogin = time(); - $user->save(); - - $_SESSION = array( - 'l' => TRUE, - 'u' => $userName, - 't' => $token, - 'r' => $user->role - ); - - exit(self::CODE_SUCCESS); - } - - private function add_user() : void - { - self::checkPermission('admin'); - - if(!isset( - $_POST['userName'], - $_POST['userEmail'], - $_POST['userRole'], - $_POST['userPassword'], - $_POST['userPasswordConfirm'] - )) - exit(self::ERROR_MISSING_ARGUMENTS); - - if(empty($_POST['userName']) || empty($_POST['userPassword']) || empty($_POST['userEmail'])) - exit(self::ERROR_EMPTY_ARGUMENTS); - - if(preg_match('/[\'^£$%&*()}{@#~?><>,|=_+¬-]/', $_POST['userName'])) - exit(self::ERROR_SPECIAL_CHARACTERS); - - if (!filter_var($_POST['userEmail'], FILTER_VALIDATE_EMAIL)) - exit(self::ERROR_INVALID_EMAIL); - - if(strlen($_POST['userPassword']) < 8) - exit(self::ERROR_PASSWORD_TOO_SHORT); - - if (!preg_match("#[0-9]+#", $_POST['userPassword']) || !preg_match("#[a-zA-Z]+#", $_POST['userPassword']) || !preg_match('/[\'^£$%&*()}{@#~?><>,|=_+¬-]/', $_POST['userPassword'])) - exit(self::ERROR_PASSWORD_TOO_SIMPLE); - - if($_POST['userPassword'] != $_POST['userPasswordConfirm']) - exit(self::ERROR_PASSWORDS_DONT_MATCH); - - $user = $this->RED->DB['users']->get(filter_var($_POST['userName'], FILTER_SANITIZE_STRING)); - - if(!empty($user->password)) - exit(self::ERROR_ENTRY_EXISTS); - - $user->save(array( - 'password' => $this->RED->encrypt($_POST['userPassword']), - 'role' => ($_POST['userRole'] == 'admin' ? 'admin' : ( $_POST['userRole'] == 'manager' ? 'manager' : 'analyst' )), - 'email' => filter_var($_POST['userEmail'], FILTER_SANITIZE_STRING) - )); - - exit(self::CODE_SUCCESS); - } - - private function save_settings() : void - { - self::checkPermission('admin'); - - if(!isset( - $_POST['site_url'], - $_POST['dashboard_url'], - $_POST['redirect_404'], - $_POST['redirect_404_url'], - $_POST['redirect_home'], - $_POST['redirect_home_url'], - $_POST['cache_redirects'], - $_POST['redirect_ssl'], - $_POST['dashboard_ssl'], - $_POST['js_redirect'], - $_POST['gtag'], - $_POST['js_redirect_after'], - $_POST['captcha_site'], - $_POST['captcha_secret'], - $_POST['language_type'], - $_POST['language_select'] - )) - exit(self::ERROR_MISSING_ARGUMENTS); - - if(!empty($_POST['redirect_404_url'])) - if(!filter_var($_POST['redirect_404_url'], FILTER_VALIDATE_URL)) - exit('e_invalid_404'); - - if(!empty($_POST['redirect_home_url'])) - if(!filter_var($_POST['redirect_home_url'], FILTER_VALIDATE_URL)) - exit('e_invalid_home'); - - $option = $this->RED->DB['options']->get('siteurl'); - $option->save(['value' => $_POST['site_url']]); - - $option = $this->RED->DB['options']->get('dashboard'); - $option->save(['value' => $_POST['dashboard_url']]); - - $option = $this->RED->DB['options']->get('redirect_404'); - $option->save(['value' => ($_POST['redirect_404'] == '1' ? true : false)]); - $option = $this->RED->DB['options']->get('redirect_404_url'); - $option->save(['value' => $_POST['redirect_404_url']]); - - $option = $this->RED->DB['options']->get('redirect_home'); - $option->save(['value' => ($_POST['redirect_home'] == '1' ? true : false)]); - $option = $this->RED->DB['options']->get('redirect_home_url'); - $option->save(['value' => $_POST['redirect_home_url']]); - - $option = $this->RED->DB['options']->get('cache_redirects'); - $option->save(['value' => ($_POST['cache_redirects'] == '1' ? true : false)]); - - $option = $this->RED->DB['options']->get('redirect_ssl'); - $option->save(['value' => ($_POST['redirect_ssl'] == '1' ? true : false)]); - $option = $this->RED->DB['options']->get('dashboard_ssl'); - $option->save(['value' => ($_POST['dashboard_ssl'] == '1' ? true : false)]); - - $option = $this->RED->DB['options']->get('js_redirect'); - $option->save(['value' => ($_POST['js_redirect'] == '1' ? true : false)]); - $option = $this->RED->DB['options']->get('gtag'); - $option->save(['value' => $_POST['gtag']]); - $option = $this->RED->DB['options']->get('js_redirect_after'); - $option->save(['value' => (int)$_POST['js_redirect_after']]); - - $option = $this->RED->DB['options']->get('captcha_site'); - $option->save(['value' => $_POST['captcha_site']]); - $option = $this->RED->DB['options']->get('captcha_secret'); - $option->save(['value' => $_POST['captcha_secret']]); - - $option = $this->RED->DB['options']->get('language_type'); - $option->save(['value' => (int)$_POST['language_type']]); - $option = $this->RED->DB['options']->get('language_select'); - $option->save(['value' => $_POST['language_select']]); - - exit(self::CODE_SUCCESS); - } - } -?> diff --git a/admin/red-config-sample.php b/admin/red-config-sample.php deleted file mode 100644 index 29f8dce..0000000 --- a/admin/red-config-sample.php +++ /dev/null @@ -1,51 +0,0 @@ - \ No newline at end of file diff --git a/admin/red-init.php b/admin/red-init.php deleted file mode 100644 index 0d1282a..0000000 --- a/admin/red-init.php +++ /dev/null @@ -1,32 +0,0 @@ - \ No newline at end of file diff --git a/admin/red-install.php b/admin/red-install.php deleted file mode 100644 index d0475a4..0000000 --- a/admin/red-install.php +++ /dev/null @@ -1,379 +0,0 @@ - - * @version $Id: red-install.php;RED_INSTALL,v beta 1.0 2019/10/27 - * @access public - */ - class RED_INSTALL - { - private $request_uri; - private $script_uri; - private $dbpath; - private $salt; - - /** - * init - * Returns the RED_INSTALL object without initializing the object - * - * @access public - * @return object RED_INSTALL - */ - public static function init() : RED_INSTALL - { - return new RED_INSTALL(); - } - - /** - * __construct - * Installs the necessary components - * - * @access public - * @return void - */ - public function __construct() - { - $HTTP = 'https://'; - if (empty($_SERVER['HTTPS'])) - $HTTP = 'http://'; - - self::check_files(); - - $this->request_uri = self::urlFix($HTTP.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); - $this->script_uri = self::urlFix($HTTP.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME'])); - - if(!isset($_POST['action'])) - if($this->script_uri != $this->request_uri) - exit(header('Location: ' . $this->script_uri)); - - if(isset($_POST['action'])) - self::do_install(); - else - self::display_page(); - } - - /** - * do_install - * Performs the necessary installation steps - * - * @return void - */ - private function do_install() : void - { - self::htaccess( - filter_var($_POST['refFolder'], FILTER_SANITIZE_STRING) - ); - - self::db_htaccess( - filter_var($_POST['refFolder'], FILTER_SANITIZE_STRING) - ); - - /** Password hash type */ - if(defined('PASSWORD_ARGON2ID')) - define('RED_ALGO', PASSWORD_ARGON2ID); - else if(defined('PASSWORD_ARGON2I')) - define('RED_ALGO', PASSWORD_ARGON2I); - else if(defined('PASSWORD_BCRYPT')) - define('RED_ALGO', PASSWORD_BCRYPT); - else if(defined('PASSWORD_DEFAULT')) - define('RED_ALGO', PASSWORD_DEFAULT); - - self::config( - filter_var($_POST['usersDB'], FILTER_SANITIZE_STRING), - filter_var($_POST['recordsDB'], FILTER_SANITIZE_STRING), - filter_var($_POST['optionsDB'], FILTER_SANITIZE_STRING) - ); - - $this->dbpath = ADMPATH.'db/'; - - self::database( - filter_var($_POST['usersDB'], FILTER_SANITIZE_STRING), - filter_var($_POST['recordsDB'], FILTER_SANITIZE_STRING), - filter_var($_POST['optionsDB'], FILTER_SANITIZE_STRING), - filter_var($_POST['defUser'], FILTER_SANITIZE_STRING), - filter_var($_POST['defPassw'], FILTER_SANITIZE_STRING), - filter_var($_POST['defaultUrl'], FILTER_SANITIZE_URL) - ); - - exit('success'); - } - - /** - * display_page - * Displays the installation page - * - * @return void - */ - private function display_page() : void - { - require_once(ADMPATH.'theme/red-install.php'); - } - - /** - * check_files - * Checks if the files already exist - * - * @return void - */ - private function check_files() : void - { - if (!is_file(ADMPATH.'theme/red-install.php')) - exit('File red-install.php.php does not exist. This file is required for installation.'); - - if (!is_file(ADMPATH.'red-config-sample.php')) - exit('File red-config-sample.php does not exist. This file is required for installation.'); - - if (!is_file(ADMPATH.'db/red-db.php')) - exit('File db/red-db.php does not exist. This file is required for installation.'); - - if (is_file(ABSPATH.'.htaccess')) - exit('File .htaccess exists. Remove it to complete the installation.'); - - if (is_file(ADMPATH.'red-config.php')) - exit('File red-config.php exists. Remove it to complete the installation.'); - } - - /** - * urlFix - * Removes unnecessary parentheses and validates the url - * - * @access private - * @param string $p - * @return string $p - */ - private function urlFix(string $p) : string - { - $p = str_replace('\\','/',trim($p)); - return (substr($p,-1)!='/') ? $p.='/' : $p; - } - - /** - * salter - * Generates random salt - * - * @access private - * @param string $length - * @return string $randomString - */ - private function salter(int $length) : string - { - $characters = '$/.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $randomString = ''; - for ($i = 0; $i < $length; $i++) {$randomString .= $characters[rand(0, 64)];} - return $randomString; - } - - /** - * config - * Creates a red-config.php file - * - * @access private - * @param string $db_users - * @param string $db_records - * @param string $db_options - * @return void - */ - private function config(string $db_users, string $db_records, string $db_options) : void - { - $config = file_get_contents( ADMPATH . 'red-config-sample.php' ); - - $this->salt = self::salter(60); - - //Salts - $config = str_replace(array( - 'example_salt', - 'example_session_salt', - 'example_nonce_salt' - ), array( - $this->salt, - self::salter(60), - self::salter(60)), - $config); - - //Cryptographic - switch (RED_ALGO) { - case 1: //PASSWORD_BCRYPT - $crypto = 'PASSWORD_BCRYPT'; - break; - case 2: //PASSWORD_ARGON2I - $crypto = 'PASSWORD_ARGON2I'; - break; - case 3: //PASSWORD_ARGON2ID - $crypto = 'PASSWORD_ARGON2ID'; - break; - default: //PASSWORD_DEFAULT - $crypto = 'PASSWORD_DEFAULT'; - break; - } - $config = str_replace( - 'PASSWORD_DEFAULT', - $crypto, - $config); - - //Databases - $config = str_replace(array( - 'users_database', - 'options_database', - 'records_database' - ), array( - $db_users, - $db_options, - $db_records), - $config); - - file_put_contents(ADMPATH.'red-config.php', $config); - } - - /** - * htaccess - * Creates a .htaccess file - * - * @access private - * @param string $dir - * @return void - */ - private function htaccess(string $dir = 'forward/') : void - { - if($dir == '/') - $dir = ''; - - $htaccess = ""; - $htaccess .= "Options All -Indexes\n\n"; - $htaccess .= "\n"; - $htaccess .= "RewriteEngine On\nRewriteBase /\nRewriteCond %{REQUEST_URI} ^(.*)$\nRewriteCond %{REQUEST_FILENAME} !-f\n"; - $htaccess .= "RewriteRule .* $dir/index.php [L]\n"; - - $path = ABSPATH.'.htaccess'; - file_put_contents($path, $htaccess); - } - - private function db_htaccess(string $dir = 'forward/') : void - { - if($dir == '/') - $dir = ''; - - $htaccess = ""; - $htaccess .= "Options All -Indexes\nIndexIgnore *\n\nErrorDocument 403 /\nErrorDocument 404 /\n\n"; - $htaccess .= "\n"; - $htaccess .= "RewriteEngine On\nRewriteRule ^(\/?)$ - [F]\n"; - $htaccess .= "RewriteRule .* $dir/index.php [L]\n"; - - $path = ABSPATH.'/'.ADMIN_PATH.'/db/.htaccess'; - file_put_contents($path, $htaccess); - } - - /** - * database - * Adds all necessary information to the database - * - * @access private - * @param string $users - * @param string $records - * @param string $options - * @param string $defUser - * @param string $defPass - * @param string $defUrl - * @return void - */ - private function database(string $users, string $records, string $options, string $defUser, string $defPass, string $defUrl) : void - { - /** Get database file */ - if (is_file(ADMPATH.'db/red-db.php')) - require_once(ADMPATH.'db/red-db.php'); - else - exit('error_2'); - - $db = new \Filebase\Database([ - 'dir' => $this->dbpath.$options, - 'backupLocation' => $this->dbpath.$options.'/backup', - 'format' => \Filebase\Format\Jdb::class, - 'cache' => true, - 'cache_expires' => 1800 - ]); - - $db_record = $db->get('siteurl'); - $db_record->save(['value' => $defUrl]); - - $db_record = $db->get('dashboard'); - $db_record->save(['value' => $defUrl.'dashboard/']); - - $db_record = $db->get('redirect_404'); - $db_record->save(['value' => false]); - $db_record = $db->get('redirect_404_url'); - $db_record->save(['value' => '']); - - $db_record = $db->get('redirect_home'); - $db_record->save(['value' => false]); - $db_record = $db->get('redirect_home_url'); - $db_record->save(['value' => '']); - - $db_record = $db->get('cache_redirects'); - $db_record->save(['value' => true]); - - $db_record = $db->get('redirect_ssl'); - $db_record->save(['value' => false]); - $db_record = $db->get('dashboard_ssl'); - $db_record->save(['value' => false]); - - $db_record = $db->get('js_redirect'); - $db_record->save(['value' => false]); - $db_record = $db->get('gtag'); - $db_record->save(['value' => '']); - $db_record = $db->get('js_redirect_after'); - $db_record->save(['value' => 0]); - - $db_record = $db->get('captcha_site'); - $db_record->save(['value' => '']); - $db_record = $db->get('captcha_secret'); - $db_record->save(['value' => '']); - - $db_record = $db->get('language_type'); - $db_record->save(['value' => 1]); - $db_record = $db->get('language_select'); - $db_record->save(['value' => 'en']); - - $db = new \Filebase\Database([ - 'dir' => $this->dbpath.$records, - 'backupLocation' => $this->dbpath.$records.'/backup', - 'format' => \Filebase\Format\Jdb::class, - 'cache' => true, - 'cache_expires' => 1800 - ]); - - $db_record = $db->get('ezk8h3'); - $db_record->save(['name' => 'EZK8H3', 'url' => 'https://github.com/rapiddev/forward', 'clicks' => 0]); - $db_record = $db->get('qubse0'); - $db_record->save(['name' => 'QUBSE0', 'url' => 'https://rdev.cc/', 'clicks' => 0]); - $db_record = $db->get('m6gmlo'); - $db_record->save(['name' => 'M6GMLO', 'url' => 'https://4geek.co/', 'clicks' => 0]); - - $db = new \Filebase\Database([ - 'dir' => $this->dbpath.$users, - 'backupLocation' => $this->dbpath.$users.'/backup', - 'format' => \Filebase\Format\Jdb::class - ]); - - $db_record = $db->get($defUser); - $db_record->email = $defUser.'@'.$_SERVER['HTTP_HOST']; - $db_record->role = 'admin'; - $db_record->password = password_hash(hash_hmac('sha256', $defPass, $this->salt), RED_ALGO); - $db_record->save(); - } - } - - RED_INSTALL::init(); - exit; -?> \ No newline at end of file diff --git a/admin/red-model.php b/admin/red-model.php deleted file mode 100644 index 3c583b3..0000000 --- a/admin/red-model.php +++ /dev/null @@ -1,499 +0,0 @@ - - * @version $Id: red-model.php;RED,v beta 1.0 2020/02/08 - * @access public - */ - class RED - { - public $DB; - - private $is_manager; - private $is_admin; - - /** - * init - * Initializes the class without instantiating it - * - * @access public - * @return object RED - */ - public static function init() : RED - { - return new RED(); - } - - /** - * __construct - * Registers the database, verifies the page id and redirect - * - * @access public - * @return void - */ - public function __construct() - { - self::decode_url(); - - self::database(); - - self::https(); - - self::page_redirect(); - } - - /** - * forward - * Performs a redirection and saves information to the database - * - * @access private - * @return void - */ - private function forward() : void - { - self::https(); - - $record = $this->DB['records']->get(strtolower(RED_PAGE)); - - if($record->url == NULL) - { - if ($this->DB['options']->get('dashboard_ssl')->value && (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off")) - { - header('HTTP/1.1 301 Moved Permanently'); - exit(header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'])); - } - $this->page(['title' => 'Page not found']); - } - - /** Languages */ - $lang = self::parse_language($_SERVER["HTTP_ACCEPT_LANGUAGE"]); - if(is_array($record->locations)) - if(array_key_exists($lang, $record->locations)) - $record->locations[$lang] += 1; - else - $record->locations[$lang] = 1; - else - $record->locations = array($lang => 1); - - /** Daily stats */ - $time = time(); - $time = array( - 'key' => date('Y-m',$time), - 'day' => date('d',$time), - ); - - if(!is_array($record->stats)) - $record->stats = array(); - - if(!array_key_exists($time['key'], $record->stats)) - $record->stats[$time['key']] = array(); - - if(!array_key_exists($time['day'], $record->stats[$time['key']])) - $record->stats[$time['key']][$time['day']] = 1; - else - $record->stats[$time['key']][$time['day']] += 1; - - /** Referrers */ - $referrers = array(); - if(is_array($record->referrers)) - $referrers = $record->referrers; - - if(isset($_SERVER['HTTP_REFERER'])) - { - $ref = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); - - if(array_key_exists($ref, $referrers)) - $referrers[$ref] += 1; - else - $referrers[$ref] = 1; - } - else - { - if(array_key_exists('direct', $referrers)) - $referrers['direct'] += 1; - else - $referrers['direct'] = 1; - } - $record->referrers = $referrers; - - /** Clicks */ - $record->clicks = $record->clicks + 1; - $record->save(); - - $this->js_forward($record->url); - - //Redirect - header('Expires: on, 01 Jan 1970 00:00:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Cache-Control: post-check=0, pre-check=0', false); - header('Pragma: no-cache'); - header('HTTP/1.1 301 Moved Permanently'); - header('Location: ' . $record->url); - exit; - } - - /** - * js_forward - * JavaScript redirection - * - * @access private - * @param string $url - * @return void - */ - private function js_forward(string $url) : void - { - if($this->DB['options']->get('js_redirect')->value) - { - $gtag = $this->DB['options']->get('gtag')->value; - - if(!empty($gtag)) - { - header('Expires: on, 01 Jan 1970 00:00:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Cache-Control: post-check=0, pre-check=0', false); - header('Pragma: no-cache'); - - $this->page([ - 'title' => 'Forward redirect', - 'page' => 'jsredirect', - 'view_data' => array( - 'url' => $url, - 'gtag' => $gtag - ) - ]); - exit; - } - } - } - - /** - * page_redirect - * Forward to page, 404 error, ajax or do forward - * - * @access private - * @return void - */ - private function page_redirect() : void - { - switch (RED_PAGE) - { - case '404': - $this->page(['title' => 'Page not found']); - break; - case '_forward_home': - $this->page(['title' => 'Home page', 'page' => 'home']); - case '_forward_dashboard': - self::admin(); - break; - break; - default: - self::forward(); - break; - } - } - - /** - * decode_url - * Analyzes and determines the current url - * - * @access private - * @return void - */ - private function decode_url(): void - { - $DIR_URL = parse_url(urldecode('/'.trim(str_replace(rtrim(dirname($_SERVER['SCRIPT_NAME']),'/'),'',$_SERVER['REQUEST_URI']),'/'))); - $DIR_URL = explode( '/', $DIR_URL['path']); - - if(!isset($DIR_URL[0], $DIR_URL[1])) - exit(RED_DEBUG ? $e : 'URL Parsing error'); - - if($DIR_URL[1] == '') - define('RED_PAGE', '_forward_home'); - else if($DIR_URL[1] == RED_DASHBOARD) - define('RED_PAGE', '_forward_dashboard'); - else - define('RED_PAGE', filter_var($DIR_URL[1], FILTER_SANITIZE_STRING)); - - if(RED_PAGE == '_forward_dashboard') - if(isset($DIR_URL[2])) - defined('RED_PAGE_DASHBOARD') or define('RED_PAGE_DASHBOARD', filter_var($DIR_URL[2], FILTER_SANITIZE_STRING)); - } - - /** - * database - * Loads database objects into an array - * - * @access private - * @return void - */ - private function database() : void - { - $this->DB = array( - 'options' => new \Filebase\Database([ - 'dir' => DB_PATH.DB_OPTIONS, - 'backupLocation' => DB_PATH.DB_OPTIONS.'/backup', - 'format' => \Filebase\Format\Jdb::class, - 'cache' => true, - 'cache_expires' => 1800 - ]), - 'records' => new \Filebase\Database([ - 'dir' => DB_PATH.DB_RECORDS, - 'backupLocation' => DB_PATH.DB_RECORDS.'/backup', - 'format' => \Filebase\Format\Jdb::class, - 'cache' => true, - 'cache_expires' => 1800 - ]) - ); - } - - /** - * https - * Checks whether the encryption enforcement option is enabled - * - * @access private - * @return void - */ - private function https() : void - { - $force = false; - - if(RED_PAGE == '_forward_dashboard' || RED_PAGE == '_forward_home' || RED_PAGE == '404') - if ($this->DB['options']->get('dashboard_ssl')->value) - $force = true; - else - if ($this->DB['options']->get('redirect_ssl')->value) - $force = true; - - if($force) - { - if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off") - { - header('HTTP/1.1 301 Moved Permanently'); - header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); - exit; - } - } - } - - /** - * admin - * Loads the administrator class - * - * @access private - * @return object RED_ADMIN - */ - private function admin() : RED_ADMIN - { - self::include(ADMPATH.'red-admin.php'); - return RED_ADMIN::init($this); - } - - /** - * page - * Checks if the RED_PAGES class exists and returns a new page object - * - * @access public - * @param array $data - * @return object RED_PAGES - */ - public function page(array $data) : RED_VIEW - { - self::include(ADMPATH.'red-view.php'); - return new RED_VIEW($data, $this); - } - - /** - * parse_language - * Checks the current language of the site - * - * @access public - * @return string key($langs) - */ - public function parse_language() : string - { - $langs = array(); - preg_match_all('~([\w-]+)(?:[^,\d]+([\d.]+))?~', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER); - - foreach($matches as $match) - { - list($a, $b) = explode('-', $match[1]) + array('', ''); - $value = isset($match[2]) ? (float) $match[2] : 1.0; - $langs[$match[1]] = $value; - - } - arsort($langs); - - if(count($langs) == 0) - return 'unknown'; - else - return key($langs); - } - - /** - * rand - * Generates a random string - * - * @access public - * @param int $length - * @return string $randomString - */ - public static function rand(int $length) : string - { - $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $randomString = ''; - for ($i = 0; $i < $length; $i++) {$randomString .= $characters[rand(0, 35)];} - return $randomString; - } - - /** - * encrypt - * Encrypts data depending on the selected method, default password - * - * @access public - * @param string $string - * @param string $type - * @return string hash_hmac() - */ - public static function encrypt(string $string, string $type = 'password') : string - { - if($type == 'password') - { - return password_hash(hash_hmac('sha256', $string, RED_SALT), RED_ALGO); - } - else if($type == 'nonce') - { - return hash_hmac('sha1', $string, RED_NONCE); - } - else if($type == 'token') - { - return hash_hmac('sha256', $string, RED_SESSION); - } - } - - /** - * compare_crypt - * Compares encrypted data with those in the database - * - * @access public - * @param string $input_string - * @param string $db_string - * @param string $type - * @param bool $plain - * @return bool true/false - */ - public static function compare_crypt(string $input_string, string $db_string, string $type = 'password', bool $plain = true) : bool - { - if($type == 'password') - { - if (password_verify(($plain ? hash_hmac('sha256', $input_string, RED_SALT) : $input_string), $db_string)) - return TRUE; - else - return FALSE; - } - else if($type == 'nonce') - { - if(($plain ? hash_hmac('sha1', $input_string, RED_NONCE) : $input_string) == $db_string) - return TRUE; - else - return FALSE; - } - else if($type == 'token') - { - if(($plain ? hash_hmac('sha256', $input_string, RED_SESSION) : $input_string) == $db_string) - return TRUE; - else - return FALSE; - } - } - - /** - * is_admin - * Verifies that the user has administrator rights based on the session and database - * - * @access public - * @return bool true/false - */ - public function is_admin() : bool - { - if($this->is_admin == NULL) - { - $this->is_admin = FALSE; - - if(isset($_SESSION['l'], $_SESSION['u'], $_SESSION['t'], $_SESSION['r'])) - { - $user = $this->DB['users']->get(filter_var($_SESSION['u'], FILTER_SANITIZE_STRING)); - - if($user->role != NULL) - if($user->role == $_SESSION['r']) - if($user->role == 'admin') - $this->is_admin = TRUE; - } - } - - if($this->is_admin) - return TRUE; - else - return FALSE; - } - - /** - * is_admin - * Verifies that the user has administrator or manager rights based on the session and database - * - * @access public - * @return bool true/false - */ - public function is_manager() : bool - { - if($this->is_manager == NULL) - { - $this->is_manager = FALSE; - - if(isset($_SESSION['l'], $_SESSION['u'], $_SESSION['t'], $_SESSION['r'])) - { - $user = $this->DB['users']->get(filter_var($_SESSION['u'], FILTER_SANITIZE_STRING)); - - if($user->role != NULL) - if($user->role == $_SESSION['r']) - if($user->role == 'admin' || $user->role == 'manager') - $this->is_manager = TRUE; - } - } - - if($this->is_manager) - return TRUE; - else - return FALSE; - } - - /** - * include - * Loads the desired file - * - * @access public - * @return file content - */ - public function include($path) : int - { - if (!is_file($path)) - exit(RED_DEBUG ? 'The '.$path.' file was not found!' : ''); - - return require_once($path); - } - } -?> diff --git a/admin/red-view.php b/admin/red-view.php deleted file mode 100644 index 28865fb..0000000 --- a/admin/red-view.php +++ /dev/null @@ -1,322 +0,0 @@ - - * @version $Id: red-view.php;RED_VIEW,v beta 1.0 2020/02/08 - * @access public - */ - class RED_VIEW - { - private $title; - private $uri; - private $mediauri; - - private $RED; - - private $LANG; - private $LANG_ARR; - - private $VIEW_DATA; - - /** - * __construct - * Displays the page - * - * @access public - * @param array $data - * @param object RED - * @return void - */ - public function __construct(array $data, RED $RED) - { - $this->RED = $RED; - - if(isset($data['page'])) - $page = $data['page']; - else - $page = '404'; - - if($page == '404') - self::error404(); - - if($page == 'home') - self::homepage(); - - if(isset($data['title'])) - $this->title = $data['title']; - else - $this->title = NULL; - - if(isset($data['view_data'])) - $this->VIEW_DATA = $data['view_data']; - - self::print_page($page); - } - - /** - * print_page - * Displays the selected page theme - * - * @access private - * @param string $name - * @return void - */ - private function print_page(string $name) : void - { - if (is_file(ADMPATH.'theme/red-'.$name.'.php')) - require_once(ADMPATH.'theme/red-'.$name.'.php'); - else - exit(RED_DEBUG ? 'Page '.$name.' file not found!' : ''); - } - - /** - * error404 - * Displays a 404 error or performs redirects - * - * @access private - * @return void - */ - private function error404() : void - { - if($this->RED->DB['options']->get('redirect_404')->value) - { - $redirect_url = $this->RED->DB['options']->get('redirect_404_url')->value; - if(!empty($redirect_url)) - { - header('HTTP/1.1 301 Moved Permanently'); - header('Location: ' . $redirect_url); - exit; - } - } - self::print_page('404'); - } - - /** - * home - * Displays the home page or performs redirects - * - * @access private - * @return void - */ - private function homepage() : void - { - if($this->RED->DB['options']->get('redirect_home')->value) - { - $redirect_url = $this->RED->DB['options']->get('redirect_home_url')->value; - if(!empty($redirect_url)) - { - header('HTTP/1.1 301 Moved Permanently'); - header('Location: ' . $redirect_url); - exit; - } - } - self::print_page('home'); - } - - /** - * home_url - * Returns the main website address - * - * @access private - * @return string $this->uri - */ - private function home_url() : string - { - if($this->uri == null) - $this->uri = $this->RED->DB['options']->get('siteurl')->value; - - return $this->uri; - } - - /** - * media_url - * Returns the URL to the media folder - * - * @access private - * @return string mediauri - */ - private function media_url() : string - { - if($this->mediauri == null) - $this->mediauri = $this->RED->DB['options']->get('siteurl')->value.RED_MEDIA; - - return $this->mediauri; - } - - /** - * title - * Returns the translated title of the site - * - * @access private - * @return string title - */ - private function title() : string - { - return RED_NAME . ($this->title != null ? ' | '.$this->e($this->title) : ''); - } - - /** - * e - * Returns a translated piece of text based on a defined language - * - * @access private - * @param string $raw_text - * @return string translated_text - */ - private function e(string $raw_text) : string - { - if($this->LANG == NULL) - { - if($this->RED->DB['options']->get('language_type')->value == 1) - $lang = $this->RED->parse_language($_SERVER['HTTP_ACCEPT_LANGUAGE']); - else - $lang = $this->RED->DB['options']->get('language_select')->value; - - switch ($lang) - { - case 'pl': case 'pl_PL': - $this->LANG = 'pl_PL'; - break; - case 'de': case 'de_DE': - $this->LANG = 'de_DE'; - break; - default: - $this->LANG = 'en_EN'; - break; - } - } - - if(file_exists(ADMPATH.'/languages/'.$this->LANG.'.json')) - $this->LANG_ARR = json_decode(file_get_contents(ADMPATH.'/languages/'.$this->LANG.'.json'), true); - - if(array_key_exists($raw_text, $this->LANG_ARR)) - return $this->LANG_ARR[$raw_text]; - else - return $raw_text; - } - - /** - * menu - * Prints the main menu of the administration panel - * - * @access private - * @return void (echo) - */ - private function menu() : void - { - $menu = array('dashboard' => array($this->e('Dashboard'), RED_DASHBOARD)); - - if($this->RED->is_admin()) - { - $menu['users'] = array($this->e('Users'), RED_DASHBOARD.'/users'); - $menu['settings'] = array($this->e('Settings'), RED_DASHBOARD.'/settings'); - } - - if(defined('RED_PAGE_DASHBOARD')) - $page = RED_PAGE_DASHBOARD; - else - $page = 'dashboard'; - - $html = ''; - - echo $html; - } - - /** - * queue_styles - * Prints all scripts - * - * @access private - * @return void - */ - private function queue_scripts() : void - { - $scripts = array( - '/js/jquery-3.4.1.js', - '/js/popper.min.js', - '/js/bootstrap.min.js', - '/js/chartist.min.js', - '/js/clipboard.min.js' - ); - - foreach ($scripts as $script) - echo ''; - } - - /** - * queue_styles - * Prints all styles - * - * @access private - * @return void - */ - private function queue_styles() : void - { - $styles = array( - 'https://fonts.googleapis.com/css?family=Montserrat:300,400,700&display=swap', - self::media_url().'/css/bootstrap.min.css', - self::media_url().'/css/red.css', - self::media_url().'/css/chartist.css' - ); - - foreach ($styles as $style) - echo ''; - } - - /** - * head - * Prints the head part of the panel page - * - * @access private - * @return void (echo) - */ - private function head() : void - { - if (is_file(ADMPATH.'theme/red-head.php')) - require_once(ADMPATH.'theme/red-head.php'); - else - echo 'Header file not found'; - } - - /** - * footer - * Prints the footer part of the panel page - * - * @access private - * @return void (echo) - */ - public function footer() : void - { - if (is_file(ADMPATH.'theme/red-footer.php')) - require_once(ADMPATH.'theme/red-footer.php'); - else - echo 'Footer file not found'; - - //Stop everything after printing theme - exit; - } - } -?> diff --git a/admin/theme/red-404.php b/admin/theme/red-404.php deleted file mode 100644 index 2199aec..0000000 --- a/admin/theme/red-404.php +++ /dev/null @@ -1,23 +0,0 @@ -head(); -?> -
- -

404!

-

e('This should not happen'); ?>

-
- -footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-about.php b/admin/theme/red-about.php deleted file mode 100644 index 28ec0b1..0000000 --- a/admin/theme/red-about.php +++ /dev/null @@ -1,80 +0,0 @@ -head(); $this->menu(); -?> -
-
-
-
- -
-

Forward

-

version

-
-
-

- Forward is a content management system created to shorten links and collect analytics. -
- It is available under the MIT license. -

- The project is available on GitHub - -
-

Used technologies & things

- -
-

- -

-
-
- - Copyright (c) 2019-2020 RapidDev -
- Leszek Pomianowski -
- https://rdev.cc/ -

- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -

- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -

- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
-
-
-
-
-
-
-footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-dashboard.php b/admin/theme/red-dashboard.php deleted file mode 100644 index 601991f..0000000 --- a/admin/theme/red-dashboard.php +++ /dev/null @@ -1,464 +0,0 @@ -head(); $this->menu(); - - /** Essential database information */ - $records = $this->RED->DB['records']->select('__id,__created_at,name,url,clicks,stats,referrers,locations')->orderBy('__created_at', 'DESC')->results(); - - /** Default values for the representation of general data */ - $total_clicks = 0; - $locations = array(); - $referrers = array(); - $top_referrer = $this->e('Unknown'); - $top_lang = $this->e('Unknown'); - - /** Current date for printing the pie chart */ - $date = array( - 'y' => date('Y', time()), - 'm' => date('m', time()), - 'd' => date('d', time()) - ); - $date['days'] = cal_days_in_month(CAL_GREGORIAN, (int)$date['m'], (int)$date['y']); - - /** Unique slug for the new URL */ - $rand = RED::rand(6); - $sucRand = true; - while ($sucRand) { - $slug = $this->RED->DB['records']->select('__id,url')->where('__id','=',strtolower($rand))->results(); - if(isset($slug[0])) - $rand = RED::rand(6); - else - $sucRand = false; - } -?> - -
-
-
-
- - - $record): - - /** Records counter */ - $c++; - - /** Total clicks */ - $total_clicks += $record['clicks']; - - /** Most popular location */ - if(is_array($record['locations'])) - foreach ($record['locations'] as $location => $count) - if(array_key_exists($location, $locations)) - $locations[$location] += $count; - else - $locations[$location] = $count; - /** Sort by most popular location */ - if(count($locations) == 0) - $locations = array($this->e('Unknown') => 0); - arsort($locations); - - /** Most popular referrer */ - if(is_array($record['referrers'])) - foreach ($record['referrers'] as $referrer => $count) - if(array_key_exists($referrer, $referrers)) - $referrers[$referrer] += $count; - else - $referrers[$referrer] = $count; - /** Sort by most popular refferer */ - if(count($referrers) == 0) - $referrers = array($this->e('Unknown') => 0); - arsort($referrers); - - /** Daily statistics */ - $stats = ''; - if(is_array($record['stats']) && isset($record['stats'][$date['y'].'-'.$date['m']])) - for ($i=1; $i <= $date['days']; $i++) - $stats .= ($i > 1 ? '/' : '').(isset($record['stats'][$date['y'].'-'.$date['m']][$i]) ? $record['stats'][$date['y'].'-'.$date['m']][$i] : '0'); - else - for($i=1; $i <= $date['days']; $i++) - $stats .= ($i > 1 ? '/': '').'0'; - $record['stats'] = $stats; - - /** Shorter URL */ - $preURL = str_replace(array('https://www.', 'https://'), array('', ''), $record['url']); - if(strlen($preURL) > 35) - $preURL = substr($preURL, 0, 35).'...'; - - /** Get the name of the most popular location */ - switch (key($locations)) - { - case 'en-us': - $top_lang = $this->e('English'); - break; - case 'pl-pl': case 'en-pl': - $top_lang = $this->e('Poland'); - break; - default: - $top_lang = key($locations); - break; - } - - /** Get the name of the most popular refferer */ - if(key($referrers) == 'direct') - $top_referrer = $this->e('Email, SMS, Direct'); - else - $top_referrer = key($referrers); - - /** Refferers for display in HTML data */ - $record_referrers = ''; - if(is_array($record['referrers'])) - foreach ($record['referrers'] as $key => $value) - $record_referrers .= (!empty($record_referrers) ? '~' : '').$key.'~'.$value; - - /** Locations for display in HTML data */ - $record_locations = ''; - if(is_array($record['locations'])) - foreach ($record['locations'] as $key => $value) - $record_locations .= (!empty($record_locations) ? '~' : '').$key.'~'.$value; - ?> - - -
-
-
-
- RED->is_manager()): ?> -
- - -
- - - -
-
-
- -
-
-
-
- -
-
-
- -
-
-
-
- -
-
    -
  • -
    - -
    -

    -

    e('Total clicks'); ?>

    -
    -
    -
  • -
  • -
    - -
    -

    -

    e('Top referrer'); ?>

    -
    -
    -
  • -
  • -
    - -
    -

    -

    e('Top language'); ?>

    -
    -
    -
  • -
-
-
-
-
-
-
-
-
-

-

- -
- RED->is_manager()): ?> -
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-RED->is_manager()): ?> - - - -footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-head.php b/admin/theme/red-head.php deleted file mode 100644 index d2a64e5..0000000 --- a/admin/theme/red-head.php +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - <?php echo self::title(); ?> - - - -
diff --git a/admin/theme/red-home.php b/admin/theme/red-home.php deleted file mode 100644 index a08284a..0000000 --- a/admin/theme/red-home.php +++ /dev/null @@ -1,59 +0,0 @@ -head(); -?> -
- - - - Forward background - -
-
-
-
-
- -
-
-
-
-
-

Forward | e('Short links manager'); ?>

-

e('Access to the site requires administrator privileges'); ?>

-
- -
- -

Forward | e('Create your own link shortener'); ?>

-
-
-
-
-
-
-
- -
- -footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-install.php b/admin/theme/red-install.php deleted file mode 100644 index 9d99e33..0000000 --- a/admin/theme/red-install.php +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - Forward | Install - - - - -
- JavaScript ERROR -

JavaScript Error
This page cannot work
correctly without JavaScript.

-
-
-
- - - - Forward background - -
-
-
-
-
- -
-
-
-
-
-
-

Install

- "> - Default URL -
- -
- Databases -
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
-
- -
- - -
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/admin/theme/red-jsredirect.php b/admin/theme/red-jsredirect.php deleted file mode 100644 index 9182db0..0000000 --- a/admin/theme/red-jsredirect.php +++ /dev/null @@ -1,61 +0,0 @@ - - - - - <?php echo self::title(); ?> - - - - - - - - - - - - - - - - - - - - - -

-

e('You will be redirected in a few seconds...'); ?>

-
- VIEW_DATA['url']; ?> - - - - \ No newline at end of file diff --git a/admin/theme/red-login.php b/admin/theme/red-login.php deleted file mode 100644 index a353b7a..0000000 --- a/admin/theme/red-login.php +++ /dev/null @@ -1,110 +0,0 @@ -head(); - - $captcha_site = $this->RED->DB['options']->get('captcha_site')->value; - $captcha_secret = $this->RED->DB['options']->get('captcha_secret')->value; -?> -
- - - - This is my face - -
-
-
-
-
- -
-
-
-
-
-
-

Sign in

- - -
' : ''; ?> -
- - -
-
- - -
- -
- -
- -
-
-
- -
-'; - echo ''; - } - -?> - -footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-settings.php b/admin/theme/red-settings.php deleted file mode 100644 index adfb88c..0000000 --- a/admin/theme/red-settings.php +++ /dev/null @@ -1,325 +0,0 @@ -head(); $this->menu(); -?> -
-
- - -
-
- -
- - -
-
-

e('URLs'); ?>

-
- - -
-
- - -
- e('Attention'); ?>!
e('Change URLs only if you have moved the site to a different domain or folder. Otherwise, access to the panel may be blocked.') ?>
-
-
-

e('Redirects'); ?>

-
- - -
-
- - -
-
-
- - -
-
- - -
-
-
-

e('Cache') ?>

-
- - -
-
-
-

Google ReCaptcha V3

-
- - -
-
- - -
- - e('You can enable %s for admin panel login.'), 'Google ReCaptcha V3'); ?> -
- e('Leave these fields blank if you want to disable ReCaptcha V3'); ?> -
-
-
-

e('Connection encryption'); ?>

-
- - -
-
- - -
- - e('You don\'t have to spend money on certificates from companies like Comodo or RapidSSL.'); ?> -
- e('You can generate a free certificate with'); ?> Let's Encrypt. -
- e('SSL is recommended.'); ?> -
- e('You protect both yourself and your users against a number of attacks. MIDM and Session Hijacking are one of the most dangerous. Never put safety second.'); ?> -
-
-
-

Google Analytics

-
- - -
-
- - -
-
- - -
- - e('Attention'); ?>! -
- e('JavaScript redirection and the use of Google Analytics may not work. This method is less effective and is not recommended.'); ?> -
- e('Anyway, if you want you can use it.'); ?> -
-
-
-

e('Languages'); ?>

-
- - -
-
- - -
-
-
-

e('Miscellaneous'); ?>

-

- e('You can change these options only in the %s file'), 'red-config.php'); ?> -
- -

-
- - - constant: RED_DASHBOARD -
-
- - - constant: RED_MEDIA -
-
- - - constant: DB_USERS -
-
- - - constant: DB_OPTIONS -
-
- - - constant: DB_RECORDS -
-
- - - constant: RED_ALGO -

e('Changing the cryptographic method will make all passwords stop working.'); ?>

-
-
-
- - - e('Remember to turn off debugging if you have stopped testing the page.'); ?> -
-
-
-
-
-
-
-
- -footer(); ?> \ No newline at end of file diff --git a/admin/theme/red-users.php b/admin/theme/red-users.php deleted file mode 100644 index 5407f9b..0000000 --- a/admin/theme/red-users.php +++ /dev/null @@ -1,198 +0,0 @@ -head(); $this->menu(); - - $users = $this->RED->DB['users']->findAll(); -?> -
-
-
-
-
e('Administrator'); ?>
- e('Has full permissions to do everything.'); ?> -
-
e('Manager'); ?>
- e('Can add and delete records. Cannot change settings or add users.'); ?> -
-
e('Analyst'); ?>
- e('Can only view data.'); ?> -
- -
-
- -
'; - $html .= '
'; - $html .= '

'.$user->getId().'

'.$user->email.'

'.( $user->role == 'admin' ? $this->e('Administrator') : ( $user->role == 'analyst' ? $this->e('Analyst') : $this->e('Manager')) ).'
'; - $html .= '
'; - $html .= ''.$this->e('Date created').':

'.$user->createdAt().'

'; - $html .= ''.$this->e('Last login').':

'.($user->lastlogin != NULL ? date('Y-m-d H:i:s', $user->lastlogin) : $this->e('Never')).'

'; - $html .= '
'; - $html .= '
'; - $html .= '
'; - echo $html; - } - - ?> -
-
-
- - - - - -footer(); ?> \ No newline at end of file diff --git a/app/assets/rdev-agent.php b/app/assets/rdev-agent.php new file mode 100644 index 0000000..59b4d79 --- /dev/null +++ b/app/assets/rdev-agent.php @@ -0,0 +1,144 @@ +DetectServer(); + $this->DetectPlaform(); + $this->IsMobile(); + } + + public function GetAgent() : string + { + return $this->agent; + } + + public function GetPlatform() : string + { + return $this->platform; + } + + public function GetServer() : string + { + return $this->server; + } + + protected function IsMobile() : void + { + // Simple browser detection + $is_lynx = false; + $is_gecko = false; + $is_winIE = false; + $is_macIE = false; + $is_opera = false; + $is_NS4 = false; + $is_safari = false; + $is_chrome = false; + $is_iphone = false; + $is_edge = false; + + if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) + { + if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'Lynx' ) !== false ) + $this->agent = 'Lynx'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'Edge' ) !== false ) + $this->agent = 'Edge'; + elseif ( stripos( $_SERVER['HTTP_USER_AGENT'], 'chrome' ) !== false ) + $this->agent = 'Chrome'; + elseif ( stripos( $_SERVER['HTTP_USER_AGENT'], 'safari' ) !== false ) + $this->agent = 'Safari'; + elseif ( ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false || strpos( $_SERVER['HTTP_USER_AGENT'], 'Trident' ) !== false ) && strpos( $_SERVER['HTTP_USER_AGENT'], 'Win' ) !== false ) + $this->agent = 'IE'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false && strpos( $_SERVER['HTTP_USER_AGENT'], 'Mac' ) !== false ) + $this->agent = 'IE'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'Gecko' ) !== false ) + $this->agent = 'Gecko'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'Opera' ) !== false ) + $this->agent = 'Opera'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'Nav' ) !== false && strpos( $_SERVER['HTTP_USER_AGENT'], 'Mozilla/4.' ) !== false ) + $this->agent = 'NS4'; + + if ( ($this->agent == 'Safari') && stripos( $_SERVER['HTTP_USER_AGENT'], 'mobile' ) !== false ) + $this->agent = 'iPhone'; + } + } + + protected function DetectPlaform() : void + { + $user_agent = $_SERVER['HTTP_USER_AGENT']; + $os_platform = 'unknown'; + + $os_array = array( + '/windows nt 10/i' => 'Windows 10', + '/windows nt 6.3/i' => 'Windows 8.1', + '/windows nt 6.2/i' => 'Windows 8', + '/windows nt 6.1/i' => 'Windows 7', + '/windows nt 6.0/i' => 'Windows Vista', + '/windows nt 5.2/i' => 'Windows Server 2003/XP x64', + '/windows nt 5.1/i' => 'Windows XP', + '/windows xp/i' => 'Windows XP', + '/windows nt 5.0/i' => 'Windows 2000', + '/windows me/i' => 'Windows ME', + '/win98/i' => 'Windows 98', + '/win95/i' => 'Windows 95', + '/win16/i' => 'Windows 3.11', + '/macintosh|mac os x/i' => 'Mac OS X', + '/mac_powerpc/i' => 'Mac OS 9', + '/linux/i' => 'Linux', + '/ubuntu/i' => 'Ubuntu', + '/iphone/i' => 'iPhone', + '/ipod/i' => 'iPod', + '/ipad/i' => 'iPad', + '/android/i' => 'Android', + '/blackberry/i' => 'BlackBerry', + '/webos/i' => 'Mobile' + ); + + foreach ($os_array as $regex => $value) + if (preg_match($regex, $user_agent)) + $os_platform = $value; + + $this->platform = $os_platform; + } + + protected function DetectServer() : void + { + /* Whether the server software is Apache or something else */ + if( strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false ) + $this->server = 'apache'; + + /* Whether the server software is Nginx or something else */ + if( strpos( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) + $this->server = 'nginx'; + + /* Whether the server software is IIS or something else */ + if( ! ($this->server == 'apache') && ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer' ) !== false ) ) + $this->server = 'IIS'; + + /* Whether the server software is IIS 7.X or greater */ + if( ($this->server == 'IIS') && intval( substr( $_SERVER['SERVER_SOFTWARE'], strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/' ) + 14 ) ) >= 7 ) + $this->server = 'IIS7'; + } + } \ No newline at end of file diff --git a/app/assets/rdev-ajax.php b/app/assets/rdev-ajax.php new file mode 100644 index 0000000..598a18a --- /dev/null +++ b/app/assets/rdev-ajax.php @@ -0,0 +1,183 @@ + + * @license MIT License + * @access public + */ + class Ajax + { + /** ERROR CODES */ + private const ERROR_MISSING_ACTION = 'e01'; + private const ERROR_MISSING_NONCE = 'e02'; + private const ERROR_INVALID_NONCE = 'e03'; + private const ERROR_INVALID_ACTION = 'e04'; + private const ERROR_INSUFFICIENT_PERMISSIONS = 'e05'; + private const ERROR_MISSING_ARGUMENTS = 'e06'; + private const ERROR_EMPTY_ARGUMENTS = 'e07'; + private const ERROR_ENTRY_EXISTS = 'e08'; + private const ERROR_ENTRY_DONT_EXISTS = 'e09'; + private const ERROR_INVALID_URL = 'e10'; + private const ERROR_INVALID_PASSWORD = 'e11'; + private const ERROR_PASSWORDS_DONT_MATCH = 'e12'; + private const ERROR_PASSWORD_TOO_SHORT = 'e13'; + private const ERROR_PASSWORD_TOO_SIMPLE = 'e14'; + private const ERROR_INVALID_EMAIL = 'e15'; + private const ERROR_SPECIAL_CHARACTERS = 'e16'; + + private const CODE_SUCCESS = 's01'; + + /** + * Forward class instance + * + * @var Forward + * @access private + */ + private $Forward; + + /** + * Current ajax action + * + * @var string + * @access private + */ + private $action = ''; + + /** + * Current ajax nonce + * + * @var string + * @access private + */ + private $nonce = ''; + + /** + * __construct + * Class constructor + * + * @access public + */ + public function __construct( $parent ) + { + $this->Forward = $parent; + + if( $this->IsNull() ) + exit('Bad gateway'); + + if ( !isset( $_POST['action'] ) ) + exit( self::ERROR_MISSING_ACTION ); + else + $this->action = filter_var( $_POST['action'], FILTER_SANITIZE_STRING ); + + if ( !isset( $_POST['nonce'] ) ) + exit( self::ERROR_MISSING_NONCE ); + else + $this->nonce = filter_var( $_POST['nonce'], FILTER_SANITIZE_STRING ); + + if( !$this->ValidNonce() ) + exit( self::ERROR_INVALID_NONCE ); + + if( !$this->ValidAction() ) + exit(self::ERROR_INVALID_ACTION); + else + $this->{$this->action}(); + + //End ajax query + $this->Forward->Session->Close(); + exit; + } + + /** + * ValidNonce + * Nonce validation + * + * @access private + * @return bool + */ + private function ValidNonce() : bool + { + if( isset( $_POST['nonce'] ) ) + if( Crypter::Compare( 'ajax_' . $this->action . '_nonce', $this->nonce, 'nonce' ) ) + return true; + else + return false; + else + return false; + } + + /** + * ValidAction + * Action validation + * + * @access private + * @return bool + */ + private function ValidAction() : bool + { + if( method_exists( $this, $this->action ) ) + return true; + else + return false; + } + + /** + * IsNull + * If $_POST is not empty + * + * @access private + * @return bool + */ + private function IsNull() : bool + { + if( !empty( $_POST ) ) + return false; + else + return true; + } + + + /** + Ajax methods + */ + + private function sign_in() : void + { + if( !isset( $_POST['login'], $_POST['password'] ) ) + exit(self::ERROR_MISSING_ARGUMENTS); + + if( empty( $_POST['login'] ) || empty( $_POST['password'] ) ) + exit(self::ERROR_ENTRY_DONT_EXISTS); + + $login = filter_var( $_POST['login'], FILTER_SANITIZE_STRING ); + $password = filter_var( $_POST['password'], FILTER_SANITIZE_STRING ); + + $user = $this->Forward->User->GetByName( $login ); + + if( empty( $user )) + $user = $this->Forward->User->GetByEmail( $login ); + + if( empty( $user )) + exit(self::ERROR_ENTRY_DONT_EXISTS); + + if( !Crypter::Compare( $password, $user['user_password'], 'password' ) ) + exit(self::ERROR_ENTRY_DONT_EXISTS); + + $this->Forward->User->LogIn( $user ); + + echo self::CODE_SUCCESS; + } + } +?> diff --git a/app/assets/rdev-crypter.php b/app/assets/rdev-crypter.php new file mode 100644 index 0000000..f948569 --- /dev/null +++ b/app/assets/rdev-crypter.php @@ -0,0 +1,120 @@ + + * @license MIT License + * @access public + */ + class Crypter + { + + /** + * Encrypt + * Encrypts data depending on the selected method, default password + * + * @access public + * @param string $string + * @param string $type + * @return string hash_hmac() + */ + public static function Encrypt(string $text, string $type = 'password') : string + { + if($type == 'password') + return password_hash( hash_hmac( 'sha256', $text, PASSWORD_SALT ), FORWARD_ALGO ); + else if($type == 'nonce') + return hash_hmac('sha1', $text, NONCE_SALT); + else if($type == 'token') + return hash_hmac('sha256', $text, SESSION_SALT); + } + + /** + * Compare + * Compares encrypted data with those in the database + * + * @access public + * @param string $text + * @param string $compare_text + * @param string $type + * @param bool $plain + * @return bool true/false + */ + public static function Compare(string $text, string $compare_text, string $type = 'password', bool $plain = true) : bool + { + if( $type == 'password' ) + { + if (password_verify(($plain ? hash_hmac('sha256', $text, PASSWORD_SALT) : $text), $compare_text)) + return true; + else + return false; + } + else if( $type == 'nonce' ) + { + if( ( $plain ? hash_hmac( 'sha1', $text, NONCE_SALT ) : $text ) == $compare_text ) + return true; + else + return false; + } + else if( $type == 'token' ) + { + if( ( $plain ? hash_hmac( 'sha256', $text, SESSION_SALT ) : $text ) == $compare_text ) + return true; + else + return false; + } + } + + public static function BaseSalter(int $length) : string + { + self::SrandSeed(); + + $characters = 'abcdefghijklmnopqrstuvwxyz0123456789GHIJKLMNOPQRSTUVWXYZABCDEF'; + $randomString = ''; + for ($i = 0; $i < $length; $i++) + $randomString .= $characters[mt_rand(0, 61)]; //Mersenne Twist + + return $randomString; + } + + public static function DeepSalter(int $length) : string + { + self::SrandSeed(); + + $characters = '+*&^%#abcdefghijklmnopqrstuvwxyz0123456789GHIJKLMNOPQRSTUVWXYZ_-=@.,?!ABCDEF'; + $randomString = ''; + for ($i = 0; $i < $length; $i++) + $randomString .= $characters[mt_rand(0, 75)]; //Mersenne Twist + + return $randomString; + } + + private static function SrandSeed() : void + { + $characters = '+MNT%#aefbcklmnQRSX67D*&^YZ_oJKLUVWpqijP-=@.z012345EFrstuvdg,?!ABChwxy89GHIO'; + $crx = ''; + for ($i = 0; $i < 50; $i++) + $crx .= $characters[mt_rand(0, 75)]; + + $rand = crc32( self::MakeSeed() . '@' . $crx ) * 2147483647; + mt_srand($rand); + } + + private static function MakeSeed() : int + { + list($usec, $sec) = explode(' ', microtime()); + return $sec + $usec * 1000000; + } + } +?> diff --git a/app/assets/rdev-dashboard.php b/app/assets/rdev-dashboard.php new file mode 100644 index 0000000..4329a2c --- /dev/null +++ b/app/assets/rdev-dashboard.php @@ -0,0 +1,150 @@ + + * @license MIT License + * @access public + */ + class Dashboard + { + /** + * Forward class instance + * + * @var Forward + * @access private + */ + private $Forward; + + /** + * Current dashboard page + * + * @var string + * @access private + */ + private $subpage; + + /** + * List of available pages + * + * @var array + * @access private + */ + private static $pages = array( + '__dashboard__', + 'ajax', + 'signout', + 'users', + 'settings', + 'about', + 'login' + ); + + /** + * __construct + * Class constructor + * + * @access public + */ + public function __construct($parent) + { + $this->Forward = $parent; + + $this->SetPage(); + $this->IfExists(); + + if( !$this->Forward->User->IsLoggedIn() ) + { + if( $this->subpage != 'login' && $this->subpage != 'ajax' ) + $this->RedirectTo( $this->Forward->Options->Get( 'login', 'login' ) ); + } + else + { + if( $this->subpage == 'login' ) + $this->RedirectTo(); + } + + switch ($this->subpage) + { + case 'ajax': + new Ajax( $this->Forward ); + break; + + case '__dashboard__': + $this->Forward->LoadModel( 'dashboard', 'Dashboard' ); + break; + + case 'login': + $this->Forward->LoadModel( 'login', 'Sign in' ); + break; + + case 'signout': + $this->Forward->User->LogOut(); + $this->Forward->Path->Redirect( $this->Forward->Options->Get( 'base_url', $this->Forward->Path->ScriptURI() ) ); + break; + + default: + $this->Forward->LoadModel( '404', 'Page not found' ); + break; + } + + //End ajax query + $this->Forward->Session->Close(); + exit; + } + + /** + * RedirectLogin + * Redirect to login if illegal dashboard page + * + * @access private + */ + private function RedirectTo( $slug = null ) : void + { + $this->Forward->Path->Redirect( + $this->Forward->Options->Get( + 'base_url', + $this->Forward->Path->ScriptURI() + ) . $this->Forward->Options->Get( 'dashboard', 'dashboard' ) . '/' . $slug + ); + } + + /** + * SetPage + * Defines current dashboard page + * + * @access private + */ + private function SetPage() : void + { + if( $this->Forward->Path->GetLevel( 1 ) == null ) + $this->subpage = '__dashboard__'; + else + $this->subpage = $this->Forward->Path->GetLevel( 1 ); + } + + /** + * IfExists + * Checks if the selected page exists + * + * @access private + */ + private function IfExists() : void + { + if( !in_array( $this->subpage, self::$pages ) ) + $this->Forward->LoadModel( '404', 'Page not found' ); + } + } +?> diff --git a/app/assets/rdev-database.php b/app/assets/rdev-database.php new file mode 100644 index 0000000..442d1e5 --- /dev/null +++ b/app/assets/rdev-database.php @@ -0,0 +1,273 @@ + + * @author Leszek Pomianowski + * @license MIT License + * @access public + */ + class Database + { + /** + * Database instance + * + * @var object + * @access protected + */ + protected $connection; + + /** + * Current query + * + * @var object + * @access protected + */ + protected $query; + + /** + * Enable showing errors + * + * @var boolean + * @access protected + */ + protected $show_errors = TRUE; + + /** + * Current query + * + * @var object + * @access protected + */ + protected $query_closed = TRUE; + + /** + * Current query + * + * @var object + * @access public + */ + public $query_count = 0; + + /** + * __construct + * Establishes a connection to the database, or returns an error + * + * @access public + */ + public function __construct() + { + $this->connection = new Mysqli(FORWARD_DB_HOST, FORWARD_DB_USER, FORWARD_DB_PASS, FORWARD_DB_NAME); + + if ($this->connection->connect_error) + $this->error('Failed to connect to MySQL - ' . $this->connection->connect_error); + + $this->connection->set_charset('utf8'); + } + + /** + * query + * Queries the database + * + * @access public + * @param string $query + * @param func_num_args (optional) + * @return object Database + */ + public function query($query) : Database + { + if (!$this->query_closed) + $this->query->close(); + + if ($this->query = $this->connection->prepare($query)) + { + if (func_num_args() > 1) + { + $x = func_get_args(); + $args = array_slice($x, 1); + $types = ''; + $args_ref = array(); + foreach ($args as $k => &$arg) + { + if (is_array($args[$k])) + { + foreach ($args[$k] as $j => &$a) + { + $types .= $this->_gettype($args[$k][$j]); + $args_ref[] = &$a; + } + } + else + { + $types .= $this->_gettype($args[$k]); + $args_ref[] = &$arg; + } + } + array_unshift($args_ref, $types); + call_user_func_array(array($this->query, 'bind_param'), $args_ref); + } + $this->query->execute(); + + if ($this->query->errno) + $this->error('Unable to process MySQL query (check your params) - ' . $this->query->error); + + $this->query_closed = FALSE; + $this->query_count++; + } + else + { + $this->error('Unable to prepare MySQL statement (check your syntax) - ' . $this->connection->error); + } + return $this; + } + + /** + * fetchAll + * Returns the result of the query + * + * @access public + * @return array $result + */ + public function fetchAll() : array + { + $params = array(); + $row = array(); + $meta = $this->query->result_metadata(); + + while ($field = $meta->fetch_field()) + $params[] = &$row[$field->name]; + + call_user_func_array(array($this->query, 'bind_result'), $params); + $result = array(); + while ($this->query->fetch()) + { + $r = array(); + foreach ($row as $key => $val) + $r[$key] = $val; + + $result[] = $r; + } + $this->query->close(); + $this->query_closed = TRUE; + return $result; + } + + /** + * fetchAll + * Returns the result array of the query + * + * @access public + * @return array $result + */ + public function fetchArray() : array + { + $params = array(); + $row = array(); + $meta = $this->query->result_metadata(); + while ($field = $meta->fetch_field()) + $params[] = &$row[$field->name]; + + call_user_func_array(array($this->query, 'bind_result'), $params); + $result = array(); + while ($this->query->fetch()) + foreach ($row as $key => $val) + $result[$key] = $val; + + $this->query->close(); + $this->query_closed = TRUE; + return $result; + } + + /** + * numRows + * Returns the number of results + * + * @access public + * @return int $num_rows + */ + public function numRows() + { + $this->query->store_result(); + return $this->query->num_rows; + } + + /** + * affectedRows + * Returns the number of rows that have been modified + * + * @access public + * @return int $affected_rows + */ + public function affectedRows() + { + return $this->query->affected_rows; + } + + /** + * lastInsertID + * Gets the identifier of the last changed row + * + * @access public + * @return int $insert_id + */ + public function lastInsertID() + { + return $this->connection->insert_id; + } + + /** + * error + * Returns an error message + * + * @access public + * @return string "error" (kills the script) + */ + public function error(string $error) : void + { + if ($this->show_errors) + exit($error); + } + + /** + * close + * Closes the database connection. + * + * @access public + * @return bool true/false + */ + public function close() : bool + { + return $this->connection->close(); + } + + /** + * _gettype + * Returns the type of the variable + * + * @access private + * @param $var + * @return string $type + */ + private function _gettype($var) : string + { + if (is_string($var)) return 's'; + if (is_float($var)) return 'd'; + if (is_int($var)) return 'i'; + return 'b'; + } + } +?> \ No newline at end of file diff --git a/app/assets/rdev-models.php b/app/assets/rdev-models.php new file mode 100644 index 0000000..3e40a87 --- /dev/null +++ b/app/assets/rdev-models.php @@ -0,0 +1,246 @@ + + * @license MIT License + * @access public + */ + class Models + { + /** + * Forward class instance + * + * @var Forward + * @access protected + */ + protected $Forward; + + /** + * Basename of view + * + * @var string + * @access protected + */ + protected $name; + + /** + * Displayed name in title + * + * @var string + * @access protected + */ + protected $displayname; + + /** + * Themes path + * + * @var string + * @access protected + */ + protected $themes; + + /** + * List of frontend styles + * + * @var array + * @access protected + */ + protected $styles; + + /** + * List of frontend scripts + * + * @var array + * @access protected + */ + protected $scripts; + + /** + * List of sites to dns prefetch + * + * @var array + * @access protected + */ + protected $prefetch; + + /** + * Root url of website + * + * @var string + * @access protected + */ + protected $baseurl; + + /** + * Nonce for secured javascript + * + * @var string + * @access protected + */ + protected $js_nonce; + + /** + * Nonce for DOM verification + * + * @var string + * @access protected + */ + protected $body_nonce; + + /** + * __construct + * Class constructor + * + * @access public + * @param Forward $parent + * @param string $name + * @param string $displayname + */ + public function __construct($parent, $name, $displayname) + { + $this->Forward = $parent; + + $this->name = $name; + $this->displayname = $displayname; + $this->themes = APPPATH . 'themes/'; + + $this->BuildNonces(); + + $this->SetBaseUrl(); + $this->SetPrefetch(); + + $this->GetStyles(); + $this->GetScripts(); + + if($_GET != NULL) + if( method_exists( $this, 'Get' ) ) + $this->Get(); + else + $this->GetView(); + + if($_POST != NULL) + if( method_exists( $this, 'Post' ) ) + if( method_exists( $this, 'Get' ) ) + $this->Get(); + else + $this->GetView(); + else + $this->GetView(); + } + + protected function _e( $text ) + { + return $this->Forward->Translator->_e( $text ); + } + + protected function AjaxGateway() + { + return $this->baseurl . $this->Forward->Options->Get( 'dashboard', 'dashboard' ) . '/ajax'; + } + + protected function AjaxNonce( $name ) + { + return Crypter::Encrypt( 'ajax_' . $name . '_nonce', 'nonce' ); + } + + protected function BuildNonces() + { + $this->body_nonce = Crypter::BaseSalter(40); + $this->js_nonce = Crypter::BaseSalter(40); + } + + protected function SetBaseUrl() + { + $this->baseurl = $this->Forward->Options->Get('base_url', $this->Forward->Path->RequestURI()); + } + + protected function GetView() + { + require_once $this->themes . "pages/rdev-$this->name.php"; + exit; + } + + protected function Title() + { + echo $this->Forward->Options->Get('site_name', 'Forward') . ($this->displayname != NULL ? ' | ' . $this->Forward->Translator->__($this->displayname) : ''); + } + + protected function Description() + { + return $this->Forward->Translator->__($this->Forward->Options->Get('site_description', 'Create your own link shortener')); + } + + protected function GetHeader() + { + require_once $this->themes . 'rdev-header.php'; + } + + protected function GetFooter() + { + require_once $this->themes . 'rdev-footer.php'; + } + + protected function GetNavigation() + { + require_once $this->themes . 'rdev-navigation.php'; + } + + protected function GetImage($name) + { + return $this->baseurl . 'media/img/' . $name; + } + + protected function SetPrefetch() + { + $this->prefetch = array( + '//ogp.me', + '//schema.org', + '//cdnjs.cloudflare.com' + ); + } + + protected function GetStyles() + { + $this->styles = array( + array( 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-alpha1/css/bootstrap.min.css', 'sha512-weZatQD41YXNCVn3Bvl2y1iAZqtH/Y+MlAQUwovog1iwj8cbSEpQMeErMnDp9CBlqIo0oxOcOF8GUEoOZYD4Zg==', '5.0.0-alpha1' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.css', 'sha512-V0+DPzYyLzIiMiWCg3nNdY+NyIiK9bED/T1xNBj08CaIUyK3sXRpB26OUCIzujMevxY9TRJFHQIxTwgzb0jVLg==', '0.11.4'), + array( $this->baseurl . 'media/css/forward.css', '', FORWARD_VERSION ) + ); + } + + protected function GetScripts() + { + $this->scripts = array( + array( 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js', 'sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==', '3.5.1' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js', 'sha512-hCP3piYGSBPqnXypdKxKPSOzBHF75oU8wQ81a6OiGXHFMeKs9/8ChbgYl7pUvwImXJb03N4bs1o1DzmbokeeFw==', '2.4.4' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-alpha1/js/bootstrap.min.js', 'sha512-lmArColmgJ0LRo8c6rZwAhB3mVVSFSsrpqOrmtXMgOFYu8VOwdxTliXrHYdsdmututXwD0Xc1GiGvZlHgNAh4g==', '5.0.0-alpha1' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.4.2/zxcvbn.js', 'sha512-TZlMGFY9xKj38t/5m2FzJ+RM/aD5alMHDe26p0mYUMoCF5G7ibfHUQILq0qQPV3wlsnCwL+TPRNK4vIWGLOkUQ==', '4.4.2' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js', 'sha512-hDWGyh+Iy4Mr9AHOzUP2+Y0iVPn/BwxxaoSleEjH/i1o4EVTF/sh0/A1Syii8PWOae+uPr+T/KHwynoebSuAhw==', '2.0.6' ), + array( 'https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.4/chartist.min.js' , 'sha512-9rxMbTkN9JcgG5euudGbdIbhFZ7KGyAuVomdQDI9qXfPply9BJh0iqA7E/moLCatH2JD4xBGHwV6ezBkCpnjRQ==', '0.11.4' ), + array( $this->baseurl . 'media/js/forward.min.js', '', FORWARD_VERSION ) + ); + } + + public function Print() + { + $this->GetView(); + $this->Forward->Session->Close(); + //Kill script :( + exit; + } + } + +?> \ No newline at end of file diff --git a/app/assets/rdev-options.php b/app/assets/rdev-options.php new file mode 100644 index 0000000..d8b0e73 --- /dev/null +++ b/app/assets/rdev-options.php @@ -0,0 +1,116 @@ + + * @license MIT License + * @access public + */ + class Options + { + /** + * Database instance + * + * @var Database + * @access private + */ + private $database; + + /** + * Options table + * + * @var array + * @access private + */ + private $options = array(); + + /** + * __construct + * Class constructor + * + * @access public + */ + public function __construct( $db ) + { + if( $db != null ) + { + $this->database = $db; + $this->Init(); + } + } + + /** + * Init + * Get options from database + * + * @access private + */ + private function Init() + { + $query = $this->database->query( 'SELECT * FROM forward_options' )->fetchAll(); + + if( !empty($query) ) + { + foreach ( $query as $option ) + { + $this->options[ $option['option_name'] ] = $option['option_value']; + } + } + } + + /** + * Update + * Update option in array and database + * + * @access public + */ + public function Update( $name, $value ) + { + if( $this->database != null ) + { + + } + } + + /** + * Get + * Get option from array + * + * @access public + */ + public function Get( $name, $default = NULL, $raw = false ) + { + if( isset( $this->options[ $name ] ) ) + { + return ($raw ? $this->options[ $name ] : $this->ParseType( $this->options[ $name ] ) ); + } + else + { + return $default; + } + } + + /** + * ParseType + * Returns parsed option to selected data type + * + * @access public + */ + private function ParseType( $option ) + { + return $option; + } + } +?> diff --git a/app/assets/rdev-redirect.php b/app/assets/rdev-redirect.php new file mode 100644 index 0000000..70e9ec7 --- /dev/null +++ b/app/assets/rdev-redirect.php @@ -0,0 +1,257 @@ + + * @license MIT License + * @access public + */ + class Redirect + { + /** + * Forward class instance + * + * @var Forward + * @access private + */ + private $Forward; + + /** + * Id of current redirected url + * + * @var string + * @access private + */ + private $id = ''; + + /** + * Name of current redirected url + * + * @var string + * @access private + */ + private $name = ''; + + /** + * Current record + * + * @var array + * @access private + */ + private $record = array(); + + /** + * __construct + * Class constructor + * + * @access public + */ + public function __construct($parent) + { + $this->Forward = $parent; + $this->ParseName(); + + if( !$this->GetRecord() ) + { + $nonexistent = $this->Forward->Options->Get( 'non_existent_record', 'error404' ); + + if( $nonexistent == 'error404' ) + { + $this->Forward->LoadModel( '404', 'Page not found' ); + } + else if( $nonexistent == 'home' ) + { + $this->Forward->LoadModel( 'home', 'Create your own link shortener' ); + } + else + { + exit; + } + } + + $this->UpdateClicks(); + $this->AddVisitor(); + + //End ajax query + $this->Forward->Session->Close(); + $this->DoFlatRedirect(); + } + + /** + * ParseName + * Prepare a redirect ID + * + * @access private + */ + private function ParseName() : void + { + $this->name = strtolower(filter_var($this->Forward->Path->GetLevel(0), FILTER_SANITIZE_STRING)); + } + + /** + * GetRecord + * Get the redirect record from the database + * + * @access private + */ + private function GetRecord() : bool + { + $query = $this->Forward->Database->query( "SELECT * FROM forward_records WHERE record_name = ?", $this->name )->fetchArray(); + + if($query == null) + { + return false; + } + else + { + $this->id = $query['record_id']; + $this->record = $query; + return true; + } + } + + /** + * ParseReferrer + * Gets the IP address of the user + * + * @access public + * @return string + */ + private function ParseIP() : string + { + if( $this->Forward->Options->Get( 'store_ip_addresses', true ) ) + { + if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) + { + return $_SERVER['HTTP_CLIENT_IP']; + } + elseif ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) + { + return $_SERVER['HTTP_X_FORWARDED_FOR']; + } + else + { + return ( !empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '' ); + } + } + else + { + return ''; + } + } + + /** + * ParseReferrer + * Gets the address from which the user came + * + * @access public + * @return string + */ + private function ParseReferrer() : string + { + if( isset( $_SERVER['HTTP_REFERER'] ) ) + { + return substr( ( !empty( $_SERVER['HTTP_REFERER'] ) ? parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) : '' ), 0, 512 ); + } + else + { + return ''; + } + } + + /** + * ParseLanguage + * Checks the current language of the site + * + * @access public + * @return string + */ + public function ParseLanguage() : string + { + $langs = array(); + preg_match_all('~([\w-]+)(?:[^,\d]+([\d.]+))?~', strtolower( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ), $matches, PREG_SET_ORDER); + + foreach($matches as $match) + { + list($a, $b) = explode('-', $match[1]) + array('', ''); + $value = isset($match[2]) ? (float) $match[2] : 1.0; + $langs[$match[1]] = $value; + + } + arsort($langs); + + if(count($langs) == 0) + return 'unknown'; + else + return substr( key($langs), 0, 128 ); + } + + /** + * UpdateClicks + * Increases the click count + * + * @access private + */ + private function UpdateClicks() : void + { + $query = $this->Forward->Database->query( + "UPDATE forward_records SET record_clicks = ? WHERE record_id = ?", + $this->record['record_clicks'] += 1, + $this->id, + ); + } + + /** + * AddVisitor + * Add current visitor to statistics database + * + * @access private + */ + private function AddVisitor() : void + { + $agent = new Agent(); + + $query = $this->Forward->Database->query( + "INSERT INTO forward_statistics_visitors (record_id, visitor_ip, visitor_origin, visitor_language, visitor_agent, visitor_platform) VALUES (?,?,?,?,?,?)", + $this->id, + $this->ParseIP(), + $this->ParseReferrer(), + $this->ParseLanguage(), + $agent->GetAgent(), + $agent->GetPlatform() + ); + } + + /** + * DoFlatRedirect + * Starts redirecting the user via php + * + * @access private + */ + private function DoFlatRedirect() : void + { + //Redirect + header('Expires: on, 01 Jan 1970 00:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Pragma: no-cache'); + header('HTTP/1.1 301 Moved Permanently'); + header('Location: ' . $this->record['record_url']); + + exit; + } + } +?> diff --git a/app/assets/rdev-session.php b/app/assets/rdev-session.php new file mode 100644 index 0000000..f254b63 --- /dev/null +++ b/app/assets/rdev-session.php @@ -0,0 +1,61 @@ + + * @license MIT License + * @access public + */ + class Session + { + /** + * Open + * Opens a new session + * + * @access public + * @return void + */ + public function Open() : void + { + session_start(); + session_regenerate_id(); + } + + /** + * Destroy + * Destroys the session and data in it + * + * @access public + * @return void + */ + public function Destroy() : void + { + session_destroy(); + } + + /** + * Open + * Closes the current session + * + * @access public + * @return void + */ + public function Close() : void + { + + } + } + +?> diff --git a/app/assets/rdev-translator.php b/app/assets/rdev-translator.php new file mode 100644 index 0000000..adde9c3 --- /dev/null +++ b/app/assets/rdev-translator.php @@ -0,0 +1,67 @@ + + * @license MIT License + * @access public + */ + class Translator + { + public $locale; + public $domain; + + private $strings_array = array(); + + public function SetLocale($locale, $domain = 'forward') : void + { + $this->locale = $locale; + $this->domain = $domain; + } + + public function Init() : void + { + if( $this->locale == NULL ) + $this->ParseLanguage(); + + if( file_exists( APPPATH . '/languages/' . $this->locale.'.json' ) ) + if( self::IsValid( APPPATH . '/languages/' . $this->locale.'.json' ) ) + $this->strings_array = json_decode( file_get_contents( APPPATH . '/languages/' . $this->locale.'.json'), true ); + } + + private static function IsValid($file) : bool + { + return true; + } + + private function ParseLanguage() : void + { + $this->locale = "pl_PL"; + } + + public function __($text) + { + if( array_key_exists( $text, $this->strings_array ) ) + return $this->strings_array[$text]; + else + return $text; + } + + public function _e($text) + { + echo $this->__($text); + } + } +?> diff --git a/app/assets/rdev-uri.php b/app/assets/rdev-uri.php new file mode 100644 index 0000000..9ddbf9e --- /dev/null +++ b/app/assets/rdev-uri.php @@ -0,0 +1,143 @@ + + * @license MIT License + * @access public + */ + class Uri + { + /** + * Current first level page + * + * @access public + * @var object + */ + public $pagenow = NULL; + + /** + * Whole URL path + * + * @access public + * @var object + */ + public $trace = array(); + + /** + * Whole URL path + * + * @access public + * @var object + */ + public $ssl = FALSE; + + public function __construct() + { + if (!empty($_SERVER['HTTPS'])) + $this->ssl = TRUE; + } + + /** + * ScriptURI + * Returns the curent request url + * + * @access public + * @return string + */ + public function RequestURI() : string + { + return self::UrlFix( ($this->ssl ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + } + + /** + * ScriptURI + * Returns the curent script url + * + * @access public + * @return string + */ + public function ScriptURI() : string + { + return self::UrlFix( ($this->ssl ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . dirname( $_SERVER['SCRIPT_NAME'] ) ); + } + + /** + * ScriptURI + * Returns the curent script url + * + * @access public + * @return string + */ + public function Redirect( $url ) : void + { + header( 'Expires: on, 01 Jan 1970 00:00:00 GMT' ); + header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); + header( 'Cache-Control: no-store, no-cache, must-revalidate' ); + header( 'Cache-Control: post-check=0, pre-check=0', false ); + header( 'Pragma: no-cache' ); + header( 'Location: ' . $url ); + + exit; + } + + /** + * GetLevel + * Returns the selected level + * + * @access public + * @return string + */ + public function GetLevel( $lvl ) : string + { + if( isset( $this->trace[ $lvl + 1 ] ) ) + return $this->trace[ $lvl + 1 ]; + else + return ''; + } + + /** + * Parse + * Analyzes and determines the current url + * + * @access public + * @return void + */ + public function Parse() : void + { + $this->trace = explode( '/', parse_url(urldecode('/'.trim(str_replace(rtrim(dirname($_SERVER['SCRIPT_NAME']),'/'),'',$_SERVER['REQUEST_URI']),'/')))[ 'path' ] ); + + if( !isset( $this->trace[ 0 ], $this->trace[ 1 ] ) ) + $this->pagenow = 'unknown'; + else + $this->pagenow = filter_var( $this->trace[ 1 ], FILTER_SANITIZE_STRING ); + } + + /** + * UrlFix + * Removes unnecessary parentheses and validates the url + * + * @access private + * @param string $p + * @return string $p + */ + private static function UrlFix(string $p) : string + { + $p = str_replace( '\\', '/', trim( $p ) ); + return ( substr( $p, -1 ) != '/' ) ? $p .= '/' : $p; + } + } + +?> \ No newline at end of file diff --git a/app/assets/rdev-user.php b/app/assets/rdev-user.php new file mode 100644 index 0000000..646ddca --- /dev/null +++ b/app/assets/rdev-user.php @@ -0,0 +1,196 @@ + + * @license MIT License + * @access public + */ + class User + { + /** + * Forward class instance + * + * @var Forward + * @access private + */ + private $Forward; + + /** + * Active user id + * + * @var int + * @access private + */ + private $id; + + /** + * Current user + * + * @var array + * @access private + */ + private $User; + + /** + * __construct + * Class constructor + * + * @access public + */ + public function __construct($parent) + { + $this->Forward = $parent; + } + + /** + * LogIn + * Sign in selected user + * + * @param array $user + * @access public + */ + public function LogIn( $user ) : void + { + $token = Crypter::Encrypt(Crypter::DeepSalter(30), 'token'); + + if( $this->Forward->Database == null ) + $this->Forward->Database = new Database(); + + $query = $this->Forward->Database->query( + "UPDATE forward_users SET user_token = ?, user_last_login = ? WHERE user_id = ?", + $token, + (new DateTime())->format('Y-m-d H:i:s'), + $user['user_id'] + ); + + $this->id = $user['user_id']; + + session_regenerate_id(); + $_SESSION = array( + 'l' => true, + 'u' => $user['user_id'], + 't' => $token, + 'r' => $user['user_role'] + ); + } + + /** + * LogOut + * Sign out selected user and destroy session + * + * @access public + */ + public function LogOut() : void + { + if( isset( $_SESSION['u'], $_SESSION['t'], $_SESSION['r'] ) ) + { + if( $this->User == null ) + $this->GetUser( $_SESSION['u'] ); + + $query = $this->Forward->Database->query( + "UPDATE forward_users SET user_token = ? WHERE user_id = ?", + '', + $this->User['user_id'], + ); + } + + $this->Forward->Session->Destroy(); + } + + /** + * IsLoggedIn + * Checks if the user is correctly logged in + * + * @access public + */ + public function IsLoggedIn() : bool + { + if( isset( $_SESSION['u'], $_SESSION['t'], $_SESSION['r'] ) ) + { + if( $this->User == null ) + $this->GetUser( $_SESSION['u'] ); + + if($this->User != null) + { + if( isset( $this->User['user_token'], $this->User['user_role'] ) ) + { + if( Crypter::Compare($_SESSION['t'], $this->User['user_token'], 'token', false) && $_SESSION['r'] == $this->User['user_role'] ) + { + return true; + } + } + } + } + + return false; + } + + public function Active() : array + { + if( $this->User == null ) + $this->GetUser( $this->id ); + + return $this->User; + } + + /** + * GetUser + * Get's user by id + * + * @param int $id + * @access public + */ + private function GetUser( $id ) + { + $query = $this->Forward->Database->query( "SELECT * FROM forward_users WHERE user_id = ?", $id )->fetchArray(); + + if($query != null) + { + $this->id = $query['user_id']; + $this->User = $query; + } + } + + /** + * GetByName + * Get's user by username + * + * @param string $username + * @access public + */ + public function GetByName( $username ) + { + $query = $this->Forward->Database->query( "SELECT user_id, user_email, user_password, user_role, user_token FROM forward_users WHERE user_name = ?", $username )->fetchArray(); + return $query; + } + + /** + * GetByEmail + * Get's user by e-mail + * + * @param string $username + * @access public + */ + public function GetByEmail( $email ) + { + $query = $this->Forward->Database->query( "SELECT user_id, user_name, user_password, user_role, user_token FROM forward_users WHERE user_email = ?", $email )->fetchArray(); + return $query; + } + } + +?> diff --git a/app/languages/de_DE.json b/app/languages/de_DE.json new file mode 100644 index 0000000..7500c63 --- /dev/null +++ b/app/languages/de_DE.json @@ -0,0 +1,300 @@ +{ + + "Short links manager": + "Short Links Manager", + + "Create your own link shortener": + "Erstelle deinen eigenen Link-Shortener", + + "Home page": + "Startseite", + + "Installer": + "Installateur", + + "Dashboard": + "Instrumententafel", + + "Users": + "Benutzer", + + "Settings": + "Einstellungen", + + "About": + "Informationen", + + "Sign in": + "Anmelden", + + "Login": + "Login", + + "You entered an incorrect login or password": + "Sie haben ein falsches Login oder Passwort eingegeben", + + "Enter username/email": + "Geben Sie Benutzernamen oder E-Mail", + + "Submit": + "Einreichen", + + "Add new": + "Neue hinzufügen", + + "Total clicks": + "Gesamtanzahl an Klicks", + + "Background image": + "Hintergrundbild", + + "Logo font": + "Logo Schriftart", + + "Access to the site requires administrator privileges": + "Der Zugriff auf die Site erfordert Administratorrechte", + + "Top referrer": + "Top Referrero", + + "Top language": + "Spitzensprache", + + "total links": + "Links insgesamt", + + "English": + "Englisch", + + "Email, SMS, Direct": + "Email, SMS, Direkt", + + "Save settings": + "Einstellungen speichern", + + "Main": + "Main", + + "Redirects": + "Umleiten", + + "Cache": + "Zwischenspeicher", + + "Encryption": + "Verschlüsselung", + + "Analytics": + "Analytics", + + "Captcha": + "Captcha", + + "Languages": + "Sprachen", + + "Miscellaneous": + "Sonstiges", + + "URLs": + "URLs", + + "Main website URL": + "URL der Hauptwebsite", + + "Dashboard URL": + "Adres kokpitu", + "Attention":"Beachtung", + + "Change URLs only if you have moved the site to a different domain or folder. Otherwise, access to the panel may be blocked.": + "Ändern Sie URLs nur, wenn Sie die Site in eine andere Domäne oder einen anderen Ordner verschoben haben. Andernfalls kann der Zugang zum Panel blockiert werden.", + + "Redirect 404 page": + "404-Seite umleiten", + + "URL to which redirect error 404": + "URL, zu der Fehler 404 umgeleitet wird", + + "Redirect Home page": + "Startseite umleiten", + + "URL to which redirect home page": + "URL, zu der die Startseite umgeleitet wird", + + "Enable Cache for records database": + "Cache für Datensatzdatenbank aktivieren", + + "ReCaptcha site key": + "Site-Schlüssel ReCaptcha", + + "ReCaptcha secret key": + "geheimer Schlüssel ReCaptcha", + + "You can enable %s for admin panel login.": + "Sie können %s für die Anmeldung im Admin-Bereich aktivieren.", + + "Leave these fields blank if you want to disable ReCaptcha V3": + "Lassen Sie diese Felder leer, wenn Sie ReCaptcha V3 deaktivieren möchten", + + "Connection encryption": + "Verbindungsverschlüsselung", + + "Force SSL connection for redirects": + "SSL-Verbindung für Umleitungen erzwingen", + + "Force SSL connection for dashboard": + "SSL-Verbindung für Dashboard erzwingen", + + "You don't have to spend money on certificates from companies like Comodo or RapidSSL.": + "Sie müssen kein Geld für Zertifikate von Unternehmen wie Comodo oder RapidSSL ausgeben.", + + "You can generate a free certificate with": + "Mit können Sie ein kostenloses Zertifikat erstellen", + + "SSL is recommended.": + "SSL wird empfohlen.y", + + "You protect both yourself and your users against a number of attacks. MIDM and Session Hijacking are one of the most dangerous. Never put safety second.": + "Sie schützen sich und Ihre Benutzer vor einer Reihe von Angriffen. MIDM und Session Hijacking gehören zu den gefährlichsten. Niemals die Sicherheit an zweiter Stelle setzen.", + + "JS Redirection": + "JS-Umleitung", + + "Tracking Code (gtag)": + "Tracking-Code (gtag)", + + "Redirect after:": + "Weiterleiten nach:", + + "Immediately": + "Sofort", + + "second": + "zweite", + + "seconds": + "sekunden", + + "eg.:": + "z.B.:", + + "JavaScript redirection and the use of Google Analytics may not work. This method is less effective and is not recommended.": + "Die JavaScript-Umleitung und die Verwendung von Google Analytics funktionieren möglicherweise nicht. Diese Methode ist weniger effektiv und wird nicht empfohlen.", + + "Anyway, if you want you can use it.": + "Wie auch immer, wenn Sie möchten, können Sie es verwenden.", + + "Choose how languages are detected": + "Wählen Sie aus, wie Sprachen erkannt werden", + + "Automatically (browser)": + "Automatisch (Browser)", + + "Automatically (geolocation)": + "Automatisch (Geolocation)", + + "Permanently defined": + "Fest definiert", + + "Statically defined language": + "Statisch definierte Sprache", + + "You can change these options only in the %s file": + "Sie können diese Optionen nur in der %s-Datei ändern", + + "Dashboard path for URL": + "Dashboard-Pfad für URL", + + "Users database": + "Benutzerdatenbank", + + "Options database": + "Optionsdatenbank", + + "Records database": + "Datensatzdatenbank", + + "Cryptographic method for passwords": + "Kryptografische Methode für Kennwörter", + + "Changing the cryptographic method will make all passwords stop working.": + "Wenn Sie die Verschlüsselungsmethode ändern, funktionieren alle Kennwörter nicht mehr.", + + "Debugging": + "Debuggen", + + "Remember to turn off debugging if you have stopped testing the page.": + "Denken Sie daran, das Debuggen zu deaktivieren, wenn Sie das Testen der Seite beendet haben.", + + "Enabled": + "Aktiviert", + + "Disabled": + "Deaktiviert", + + "Edit": + "Bearbeiten", + + "Delete": + "Löschen", + + "Never": + "Noch nie", + + "Unknown": + "Unbekannt", + + "Add new user": + "Neuen Benutzer hinzufügen", + + "Date created": + "Datum erstellt", + + "Last login": + "Letzte Anmeldung", + + "Administrator": + "Administrator", + + "Analyst": + "Analytiker", + + "Manager": + "Manager", + + "Has full permissions to do everything.": + "Hat die vollen Berechtigungen, um alles zu tun.", + + "Can add and delete records. Cannot change settings or add users.": + "Kann Datensätze hinzufügen und löschen. Einstellungen können nicht geändert oder Benutzer hinzugefügt werden.", + + "Can only view data.": + "Kann nur Daten anzeigen.", + + "Something went wrong!": + "Etwas ist schief gelaufen!", + + "Success!": + "Erfolg!", + + "Settings have been saved.": + "Einstellungen wurden gespeichert.", + + "Holy guacamole!": + "Error!", + + "New link was added.": + "Neuer Link wurde hinzugefügt.", + + "You must provide a URL!": + "Sie müssen eine URL angeben!", + + "The URL you entered is not valid!": + "Die eingegebene URL ist ungültig!", + + "The 404 page redirect URL is not valid!": + "Die Weiterleitungs-URL für 404-Seiten ist ungültig!", + + "The home page redirect URL is not valid!": + "Die Weiterleitungs-URL der Homepage ist ungültig!" +} \ No newline at end of file diff --git a/admin/languages/en_EN.json b/app/languages/en_US.json similarity index 100% rename from admin/languages/en_EN.json rename to app/languages/en_US.json diff --git a/app/languages/pl_PL.json b/app/languages/pl_PL.json new file mode 100644 index 0000000..5affed5 --- /dev/null +++ b/app/languages/pl_PL.json @@ -0,0 +1,378 @@ +{ + + "Short links manager": + "Manadżer linków", + + "Create your own link shortener": + "Stwórz swój własny skracacz adresów", + + "Home page": + "Strona główna", + + "Installer": + "Instalator", + + "Dashboard": + "Kokpit", + + "Users": + "Użytkownicy", + + "Settings": + "Ustawienia", + + "About": + "Informacje", + + "Sign in": + "Zaloguj się", + + "Login": + "Login", + + "You entered an incorrect login or password": + "Podałeś nieprawidłowy login lub hasło", + + "Enter username/email": + "Wprowadź nazwę użytkownika lub email", + + "Submit": + "Zatwierdź", + + "Add new": + "Dodaj nowy", + + "Total clicks": + "Kliknięcia", + + "Background image": + "Obrazek w tle", + + "Logo font": + "Krój pisma logo", + + "Access to the site requires administrator privileges": + "Dostęp do witryny wymaga uprawnień administratora", + + "Top referrer": + "Najpopularniejsze źródło", + + "Top language": + "Najpopularniejszy język", + + "total links": + "wszystkich odnośników", + + "English": + "Angielski", + + "Poland": + "Polska", + + "Email, SMS, Direct": + "Email, SMS, Bezpośredni", + + "Save settings": + "Zapisz ustawienia", + + "Main": + "Główne", + + "Redirects": + "Przekierowania", + + "Cache": + "Pamięć podręczna", + + "Encryption": + "Szyfrowanie", + + "Analytics": + "Analityka", + + "Captcha": + "Captcha", + + "Languages": + "Języki", + + "Miscellaneous": + "Dodatkowe", + + "URLs": + "Adresy", + + "Main website URL": + "Główny adres witryny", + + "Dashboard URL": + "Adres kokpitu", + "Attention":"Uwaga", + + "Change URLs only if you have moved the site to a different domain or folder. Otherwise, access to the panel may be blocked.": + "Zmień adresy tylko jeśli witryna została przeniesiona do innego folderu lub domeny. W innym przypadku, dostęp do panelu może zostać zablokowany.", + + "Redirect 404 page": + "Przekieruj stronę 404", + + "URL to which redirect error 404": + "Adres, na który przekierować błąd 404", + + "Redirect Home page": + "Przekieruj stronę główną", + + "URL to which redirect home page": + "Adres, na który przekierować stronę główną", + + "Enable Cache for records database": + "Włącz pamięć podręczną dla bazy danych rekordów", + + "ReCaptcha site key": + "Klucz strony ReCaptcha", + + "ReCaptcha secret key": + "Sekretny klucz ReCaptcha", + + "You can enable %s for admin panel login.": + "Możesz włączyć %s dla logowania do kokpitu.", + + "Leave these fields blank if you want to disable ReCaptcha V3": + "Pozostaw te pola puste, jeśli chcesz wyłączyć ReCaptcha V3", + + "Connection encryption": + "Szyfrowanie połączenia", + + "Force SSL connection for redirects": + "Wymuś połączenie SSL dla przekierowań", + + "Force SSL connection for dashboard": + "Wymuś połączenie SSL dla kokpitu", + + "You don't have to spend money on certificates from companies like Comodo or RapidSSL.": + "Nie musisz wydawać pieniędzy na certyfikaty od firm jak Comodo albo RapidSSL", + + "You can generate a free certificate with": + "Możesz wygenerować certyfikat za darmo z pomocą", + + "SSL is recommended.": + "SSL jest rekomendowany", + + "You protect both yourself and your users against a number of attacks. MIDM and Session Hijacking are one of the most dangerous. Never put safety second.": + "Chronisz siebie i swoich użytkowników przed atakami. MIDM i Hijacking sesji są jednymi z bardziej groźnych. Nigdy nie stawiaj bezpieczeństwa na drugim miejscu", + + "JS Redirection": + "Przekierowanie przez JS", + + "Tracking Code (gtag)": + "Kod śledzenia (gtag)", + + "Redirect after:": + "Przekieruj po:", + + "Immediately": + "Natychmiast", + + "second": + "sekundzie", + + "seconds": + "sekundach", + + "eg.:": + "np:", + + "JavaScript redirection and the use of Google Analytics may not work. This method is less effective and is not recommended.": + "Przekierowanie przez JavaScript i używanie Google Analytics może nie działać. Ta metoda jest mniej efektywna i nie jest rekomendowana.", + + "Anyway, if you want you can use it.": + "Bądź co bądź, jeśli chcesz, możesz jej użyć", + + "Choose how languages are detected": + "Wybierz jak język ma być wykrywany", + + "Automatically (browser)": + "Automatycznie (przeglądarka)", + + "Automatically (geolocation)": + "Automatycznie (geolokalizacja)", + + "Permanently defined": + "Zdefiniowany na stałe", + + "Statically defined language": + "Statycznie wybrany język", + + "You can change these options only in the %s file": + "Możesz zmienić te ustawienia tylko w pliku %s", + + "Dashboard path for URL": + "Ścieżka dla URL kokpitu", + + "Users database": + "Baza danych użytkowników", + + "Options database": + "Baza danych opcji", + + "Records database": + "Baza danych rekordów", + + "Cryptographic method for passwords": + "Metoda kryptograficzna dla haseł", + + "Changing the cryptographic method will make all passwords stop working.": + "Zmiana metody kryptograficznej sprawi, że wszystkie hasła przestaną działać.", + + "Debugging": + "Debugowanie", + + "Remember to turn off debugging if you have stopped testing the page.": + "Pamiętaj, aby wyłączyć debugowanie kiedy skończysz testować witrynę.", + + "Enabled": + "Włączone", + + "Disabled": + "Wyłączone", + + "Edit": + "Edytuj", + + "Delete": + "Usuń", + + "Never": + "Nigdy", + + "Close": + "Zamknij", + + "Unknown": + "Nieznany", + + "Add user": + "Dodaj użytkownika", + + "Delete user": + "Usuń użytkownika", + + "Add new user": + "Dodaj nowego użytkownika", + + "Date created": + "Data utworzenia", + + "Last login": + "Ostatnie logowanie", + + "Administrator": + "Administrator", + + "Analyst": + "Analityk", + + "Manager": + "Menedżer", + + "Has full permissions to do everything.": + "Ma pełne uprawnienia, aby robić wszystko", + + "Can add and delete records. Cannot change settings or add users.": + "Może dodawać i usuwać rekordy. Nie może zmieniać ustawień ani dodawać użytkowników", + + "Can only view data.": + "Może tylko przeglądać dane", + + "Something went wrong!": + "Coś poszło nie tak!", + + "Success!": + "Sukces!", + + "Settings have been saved.": + "Ustawienia zostały zapisane", + + "Holy guacamole!": + "Motyla noga!", + + "New link was added.": + "Nowy link został dodany.", + + "You must provide a URL!": + "Musisz podać adres URL", + + "The URL you entered is not valid!": + "Podany adres jest nieprawidłowy!", + + "The 404 page redirect URL is not valid!": + "Adres przekierowania strony 404 jest nieprawidłowy!", + + "The home page redirect URL is not valid!": + "Adres przekierowania strony głównej jest nieprawidłowy!", + + "A new user has been added.": + "Nowy użytkownik został dodany.", + + "Login, password and email fields are required!": + "Pola login, hasło i email są wymagane!", + + "Login field contains illegal characters!": + "Nazwa użytkownika zawiera niedozwole znaki!", + + "Email field is invalid!": + "Pole e-mail jest nieprawidłowe!", + + "Password is too short!": + "Hasło jest za krótkie!", + + "The password is too simple. Use letters, numbers and special characters!": + "Hasło jest za proste. Użyj liter, cyfr oraz znaków specjalnych!", + + "Passwords do not match!": + "Hasła nie są jednakowe!", + + "User with this login already exists!": + "Użytkownik o tym loginie już istnieje!", + + "Delete record": + "Usuń rekord", + + "Cancel": + "Anuluj", + + "Are you sure you want to delete the %s record?": + "Na pewno chcesz usunąć rekord %s?", + + "Username": + "Nazwa użytkownika", + + "Email address": + "Adres email", + + "Link has been copied to your clipboard": + "Link został skopiowany do Twojego schowka", + + "A record with this ID already exists!": + "Rekord z podanym ID już istnieje!", + + "Role": + "Rola", + + "Referrers": + "Referenci", + + "Locations": + "Lokalizacje", + + "Password": + "Hasło", + + "Confirm password": + "Potwierdź hasło", + + "This should not happen": + "To nie powinno mieć miejsca", + + "You will be redirected in a few seconds...": + "Zostaniesz przekierowany za kilka sekund..." +} \ No newline at end of file diff --git a/app/loader.php b/app/loader.php new file mode 100644 index 0000000..40325e5 --- /dev/null +++ b/app/loader.php @@ -0,0 +1,55 @@ + diff --git a/app/models/rdev-install.php b/app/models/rdev-install.php new file mode 100644 index 0000000..7acce94 --- /dev/null +++ b/app/models/rdev-install.php @@ -0,0 +1,361 @@ + + * @license MIT License + * @access public + */ + class Model extends Models + { + /** + * Get + * Get install form + * + * @access private + */ + public function Get() + { + $this->InstallForm(); + exit; + } + + /** + * Post + * Get install form + * + * @access private + */ + public function Post() + { + $this->InstallForm(); + exit; + } + + /** + * InstallForm + * Parse and verify install form + * + * @access private + */ + private function InstallForm() + { + $result = array( + 'status' => 'error', + 'message' => 'Something went wrong!' + ); + + if (!isset( + $_POST['action'], + $_POST['input_scriptname'], + $_POST['input_baseuri'], + $_POST['input_db_name'], + $_POST['input_db_user'], + $_POST['input_db_host'], + $_POST['input_db_password'], + $_POST['input_user_name'], + $_POST['input_user_password'] + )) + { + $result['message'] = 'Missing fields'; + exit(json_encode($result)); + } + + if($_POST['action'] != 'setup') + { + $result['message'] = 'Inavlid action'; + exit(json_encode($result)); + } + + if(trim($_POST['input_user_name']) === '') + { + $result['message'] = 'User name empty'; + exit(json_encode($result)); + } + + if(trim($_POST['input_user_password']) === '') + { + $result['message'] = 'Password empty'; + exit(json_encode($result)); + } + + if(trim($_POST['input_db_name']) === '') + { + $result['message'] = 'DB name empty'; + exit(json_encode($result)); + } + + if(trim($_POST['input_db_user']) === '') + { + $result['message'] = 'DB user empty'; + exit(json_encode($result)); + } + + if(trim($_POST['input_db_host']) === '') + { + $result['message'] = 'DB host empty'; + exit(json_encode($result)); + } + + //error_reporting(0); + $database = new Mysqli($_POST['input_db_host'], $_POST['input_db_user'], $_POST['input_db_password'], $_POST['input_db_name']); + if ($database->connect_error) + { + $result['message'] = 'Unable to connect to database'; + exit(json_encode($result)); + } + + $this->BuildResources($database, array( + 'path' => filter_var($_POST['input_scriptname'], FILTER_SANITIZE_STRING), + 'db_name' => filter_var($_POST['input_db_name'], FILTER_SANITIZE_STRING), + 'db_host' => filter_var($_POST['input_db_host'], FILTER_SANITIZE_STRING), + 'db_user' => filter_var($_POST['input_db_user'], FILTER_SANITIZE_STRING), + 'db_pass' => $_POST['input_db_password'], + 'baseuri' => filter_var($_POST['input_baseuri'], FILTER_SANITIZE_STRING), + 'user_name' => filter_var($_POST['input_user_name'], FILTER_SANITIZE_STRING), + 'user_pass' => $_POST['input_user_password'], + )); + + $result['status'] = 'success'; + exit(json_encode($result)); + } + + /** + * BuildResources + * Creates config file and database tables + * + * @param Mysqli $database + * @param array $args + * @access private + */ + private function BuildResources($database, $args) : void + { + $this->BuildHtaccess($args['path']); + $this->BuildConfig($args); + $this->BuildTables($database, $args); + } + + /** + * SetAlgo + * Defines Password hash type + * + * @access private + * @return void + */ + private function SetAlgo() : string + { + /** Password hash type */ + if(defined('PASSWORD_ARGON2ID')) + return 'PASSWORD_ARGON2ID'; + else if(defined('PASSWORD_ARGON2I')) + return 'PASSWORD_ARGON2I'; + else if(defined('PASSWORD_BCRYPT')) + return 'PASSWORD_BCRYPT'; + else if(defined('PASSWORD_DEFAULT')) + return 'PASSWORD_DEFAULT'; + } + + /** + * BuildHtaccess + * Creates a .htaccess file + * + * @access private + * @param string $dir + * @return void + */ + private function BuildHtaccess(string $dir = '/') : void + { + if($dir == '/') + $dir = ''; + + $htaccess = ""; + $htaccess .= "Options All -Indexes\n\n"; + $htaccess .= "\n"; + $htaccess .= "RewriteEngine On\nRewriteBase /\nRewriteCond %{REQUEST_URI} ^(.*)$\nRewriteCond %{REQUEST_FILENAME} !-f\n"; + $htaccess .= "RewriteRule .* $dir/index.php [L]\n"; + + $path = ABSPATH . '.htaccess'; + file_put_contents( $path, $htaccess ); + } + + /** + * BuildConfig + * Creates config file + * + * @access private + * @param array $args + * @return void + */ + private function BuildConfig($args) : void + { + + $config = ""; + $config .= "SetAlgo() . " );"; + + $config .= "\n\n\t/** Database table */\n\tdefine( 'FORWARD_DB_NAME', '" . $args['db_name'] . "' );"; + $config .= "\n\t/** Database table */\n\tdefine( 'FORWARD_DB_HOST', '" . $args['db_host'] . "' );"; + $config .= "\n\t/** Database table */\n\tdefine( 'FORWARD_DB_USER', '" . $args['db_user'] . "' );"; + $config .= "\n\t/** Database table */\n\tdefine( 'FORWARD_DB_PASS', '" . $args['db_pass'] . "' );"; + + $config .= "\n\n\t/** Session salt */\n\tdefine( 'SESSION_SALT', '" . Crypter::DeepSalter(50) . "' );"; + $config .= "\n\t/** Passowrd salt */\n\tdefine( 'PASSWORD_SALT', '" . Crypter::DeepSalter(50) . "' );"; + $config .= "\n\t/** Nonce salt */\n\tdefine( 'NONCE_SALT', '" . Crypter::DeepSalter(50) . "' );"; + + $config .= "\n\n\t/** Debugging */\n\tdefine( 'FORWARD_DEBUG', FALSE );"; + + $config .= "\n\n?>\n"; + + $path = APPPATH . 'config.php'; + file_put_contents( $path, $config ); + } + + /** + * BuildTables + * Creates database tables + * + * @access private + * @param Mysqli $database + * @param array $args + * @return void + */ + private function BuildTables($database, $args) : void + { + $database->set_charset('utf8'); + + $dbFile = file( APPPATH . 'system/rdev-database.sql' ); + $queryLine = ''; + + // Loop through each line + foreach ($dbFile as $line) + { + //Skip comments and blanks + if (substr($line, 0, 2) == '--' || $line == '' || substr($line, 0, 1) == '#') + continue; + + $queryLine .= $line; + + if (substr(trim($line), -1, 1) == ';') + { + $database->query($queryLine); + $queryLine = ''; + } + } + + $this->FillData($database, $args); + } + + /** + * BuildTables + * Creates database tables + * + * @access private + * @param Mysqli $db + * @param array $args + * @return void + */ + private function FillData($database, $args) + { + //$stmt = $db->prepare("SELECT * FROM myTable WHERE name = ? AND age = ?"); + + require_once APPPATH . 'config.php'; + + //Static + $database->query("INSERT IGNORE INTO forward_options (option_name, option_value) VALUES " . + "('version', '" . FORWARD_VERSION . "'), " . + "('site_name', 'Forward'), " . + "('site_description', 'Create your own link shortener'), " . + "('dashboard', 'dashboard'), " . + "('login', 'login'), " . + "('date_format', 'j F Y'), " . + "('time_format', 'H:i'), " . + "('charset', 'UTF8'), " . + "('dashboard_links', '30'), " . + "('dashboard_sort', 'date'), " . + "('timezone', 'UTC'), " . + "('cache', 'false'), " . + "('dashboard_captcha', 'false'), " . + "('dashboard_captcha_public', ''), " . + "('dashboard_captcha_secret', ''), " . + "('force_dashboard_ssl', 'false'), " . + "('store_ip_addresses', 'true'), " . + "('force_redirect_ssl', 'false'), " . + "('non_existent_record', 'error404'), " . + "('js_redirect_after', '2000'), " . + "('js_redirect', 'false')" + ); + + $q1 = Crypter::BaseSalter(6); + $q2 = Crypter::BaseSalter(6); + $q3 = Crypter::BaseSalter(6); + + $database->query("INSERT IGNORE INTO forward_records (record_name, record_display_name, record_url) VALUES " . + "('" . strtolower( $q1 ) . "', '" . strtoupper( $q1 ) . "', 'https://github.com/rapiddev/Forward'), " . + "('" . strtolower( $q2 ) . "', '" . strtoupper( $q2 ) . "', 'https://rdev.cc/'), " . + "('" . strtolower( $q3 ) . "', '" . strtoupper( $q3 ) . "', 'https://4geek.co/')" + ); + + //Binded + if($query = $database->prepare("INSERT IGNORE INTO forward_options (option_name, option_value) VALUES ('base_url', ?)")) + { + $query->bind_param('s', $args['baseuri']); + $query->execute(); + } + + if($query = $database->prepare("INSERT IGNORE INTO forward_options (option_name, option_value) VALUES ('ssl', ?)")) + { + $ssl = $this->Forward->Path->ssl ? 'true' : 'false'; + $query->bind_param('s', $ssl); + $query->execute(); + } + + if($query = $database->prepare("INSERT IGNORE INTO forward_users (user_name, user_display_name, user_password, user_token, user_role, user_status) VALUES (?, ?, ?, ?, ?, ?)")) + { + + $password = Crypter::Encrypt($args['user_pass'], 'password'); + $token = ''; + $role = 'admin'; + $status = 1; + + $query->bind_param('ssssss', + $args['user_name'], + $args['user_name'], + $password, + $token, + $role, + $status + ); + $query->execute(); + } + + $this->Forward->User->LogIn(array('user_id' => 1, 'user_role' => 'admin')); + + $database->close(); + } + + /** + * Footer + * Prints data in footer + * + * @access private + */ + public function Footer() + { + //echo 'script'; + } + } \ No newline at end of file diff --git a/app/system/rdev-database.sql b/app/system/rdev-database.sql new file mode 100644 index 0000000..0ff61f5 --- /dev/null +++ b/app/system/rdev-database.sql @@ -0,0 +1,59 @@ + +-- Options table +CREATE TABLE IF NOT EXISTS forward_options ( + option_name VARCHAR(64) NOT NULL PRIMARY KEY, + option_value LONGTEXT +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Sessions table +CREATE TABLE IF NOT EXISTS forward_sessions ( + session_id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_id INT(6), + session_key INT(20) NOT NULL, + session_content LONGTEXT +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Records table +CREATE TABLE IF NOT EXISTS forward_records ( + record_id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, + record_name VARCHAR(256) NOT NULL, + record_display_name VARCHAR(256) NOT NULL UNIQUE, + record_description LONGTEXT, + record_url LONGTEXT, + record_author INT(6) DEFAULT 1, + record_clicks INT(20) DEFAULT 0, + record_active BOOLEAN DEFAULT true, + record_updated DATETIME DEFAULT CURRENT_TIMESTAMP, + record_created DATETIME DEFAULT CURRENT_TIMESTAMP +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Statistics visitors table +CREATE TABLE IF NOT EXISTS forward_statistics_visitors ( + visitor_id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, + record_id INT(6) UNSIGNED NOT NULL, + CONSTRAINT fk_record_id FOREIGN KEY (record_id) REFERENCES forward_records(record_id), + visitor_ip VARCHAR(128), + visitor_origin LONGTEXT, + visitor_language VARCHAR(128), + visitor_agent VARCHAR(128), + visitor_platform VARCHAR(128), + visitor_visits INT(20) DEFAULT 1, + visitor_date DATETIME DEFAULT CURRENT_TIMESTAMP +) CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Statistics master table +-- + +-- Users table +CREATE TABLE IF NOT EXISTS forward_users ( + user_id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_name VARCHAR(128) NOT NULL, + user_display_name VARCHAR(128), + user_email VARCHAR(256), + user_password VARCHAR(1024) NOT NULL, + user_token VARCHAR(256), + user_role VARCHAR(256), + user_status INT(2) NOT NULL DEFAULT 0, + user_registered DATETIME DEFAULT CURRENT_TIMESTAMP, + user_last_login DATETIME +) CHARACTER SET utf8 COLLATE utf8_general_ci; \ No newline at end of file diff --git a/app/system/rdev-forward.php b/app/system/rdev-forward.php new file mode 100644 index 0000000..9389369 --- /dev/null +++ b/app/system/rdev-forward.php @@ -0,0 +1,282 @@ + + * @license MIT License + * @access public + */ + class Forward + { + /** + * Information about the address from the Uri class + * + * @var Uri + * @access public + */ + public $Path; + + /** + * Information about the session from the Session class + * + * @var Session + * @access public + */ + public $Session; + + /** + * Class for translating text strings + * + * @var Translator + * @access public + */ + public $Translator; + + /** + * A global class that stores options + * + * @var Options + * @access public + */ + public $Options; + + /** + * A set of user management tools + * + * @var User + * @access public + */ + public $User; + + /** + * Master database instance, requires config.php + * + * @var Database + * @access public + */ + public $Database; + + /** + * __construct + * Triggers and instances all necessary classes + * + * @access public + * @return Forward + */ + public function __construct() + { + $this->Init(); + + //If the configuration file does not exist or is damaged, start the installation + if( !DEFINED( 'FORWARD_DB_NAME' ) ) + { + $this->LoadModel( 'install', 'Installer' ); + } + else + { + //Mechanism of action depending on the first part of the url + switch ( $this->Path->GetLevel( 0 ) ) + { + case '': + case null: + $this->LoadModel( 'home', 'Create your own link shortener' ); + break; + + case $this->Options->Get('dashboard', 'dashboard'): + case $this->Options->Get('login', 'login'): + new Dashboard( $this ); + break; + + default: + new Redirect( $this ); + break; + } + } + + exit; //Just in case + } + + /** + * Init + * Instances all necessary classes + * + * @access private + * @return void + */ + private function Init() : void + { + $this->InitPath(); + $this->InitSession(); + $this->InitTranslator(); + $this->InitDatabase(); + $this->InitOptions(); + $this->InitUser(); + } + + /** + * IsConfig + * Checks if the configuration file exists + * + * @access private + * @return bool + */ + private function IsConfig() : bool + { + if ( is_file( APPPATH . 'config.php' ) ) + return true; + else + return false; + } + + /** + * InitPath + * Initializes the Uri class + * + * @access private + * @return void + */ + private function InitPath() : void + { + $this->Path = new Uri(); + $this->Path->Parse(); + } + + /** + * InitSession + * Initializes the Session class + * + * @access private + * @return void + */ + private function InitSession() : void + { + $this->Session = new Session(); + $this->Session->Open(); + } + + /** + * InitTranslator + * Initializes the Translator class + * + * @access private + * @return void + */ + private function InitTranslator() : void + { + $this->Translator = new Translator(); + $this->Translator->Init(); + } + + /** + * InitDatabase + * Initializes the Database class + * + * @access private + * @return void + */ + private function InitDatabase() : void + { + if( $this->IsConfig() ) + $this->Database = new Database(); + else + $this->Database = null; + } + + /** + * InitOptions + * Initializes the Options class + * + * @access private + * @return void + */ + private function InitOptions() : void + { + $this->Options = new Options( $this->Database ); + } + + /** + * InitUser + * Initializes the User class + * + * @access private + * @return void + */ + private function InitUser() : void + { + $this->User = new User( $this ); + } + + /** + * LoadModel + * Loads the page model (logic) + * The page model is inherited from assets/rdev-models.php + * + * @access private + * @return void + */ + public function LoadModel( $name, $displayname = null ) + { + if ( is_file( APPPATH . "/models/rdev-$name.php" ) ) + { + require_once APPPATH . "/models/rdev-$name.php"; + (new Model( $this, $name, $displayname ))->Print(); + } + else + { + if( is_file( APPPATH . "/themes/pages/rdev-$name.php" ) ) + { + //Display the page without additional logic + (new Models( $this, $name, $displayname ))->Print(); + } + else + { + exit( "Unable to find model '$name'" ); + } + } + } + + /** + * Error + * Custom error handling + * + * @param string $message + * @access public + * @return html (error message) + */ + public function Error($message, $kill = false) + { + $r_message = '
Forward Error

' . date('Y-m-d h:i:s a', time()) . '
'; + + if ( is_file( APPPATH . 'config.php' ) ) + { + if( DEFINED( 'FORWARD_DEBUG' ) ) + { + if( FORWARD_DEBUG ) + echo $r_message . $message; + } + else + { + echo $r_message . $message . '
Configuration file EXISTS but FORWARD_DEBUG constant could not be found ...'; + } + } + else + { + echo $r_message . $message . '
Configuration file does not exist...'; + } + + if($kill) + exit; + } + } \ No newline at end of file diff --git a/admin/db/red-db.php b/app/system/rdev-init.php similarity index 61% rename from admin/db/red-db.php rename to app/system/rdev-init.php index bf870c2..71b6f85 100644 --- a/admin/db/red-db.php +++ b/app/system/rdev-init.php @@ -3,12 +3,15 @@ * @package Forward * * @author RapidDev - * @copyright Copyright (c) 2019, RapidDev + * @copyright Copyright (c) 2019-2020, RapidDev * @link https://www.rdev.cc/forward * @license https://opensource.org/licenses/MIT */ namespace Forward; defined('ABSPATH') or die('No script kiddies please!'); - require_once("vendor/autoload.php"); -?> \ No newline at end of file + require_once __dir__ . '/../loader.php'; + + /* Start Forward CMS */ + (new Forward()); +?> diff --git a/app/themes/pages/rdev-404.php b/app/themes/pages/rdev-404.php new file mode 100644 index 0000000..7704d78 --- /dev/null +++ b/app/themes/pages/rdev-404.php @@ -0,0 +1,25 @@ +GetHeader(); +?> +
+ +

404!

+

_e('This should not happen'); ?>

+
+ +GetFooter(); +?> diff --git a/admin/theme/red-footer.php b/app/themes/pages/rdev-dashboard.php similarity index 60% rename from admin/theme/red-footer.php rename to app/themes/pages/rdev-dashboard.php index d605458..78ed97b 100644 --- a/admin/theme/red-footer.php +++ b/app/themes/pages/rdev-dashboard.php @@ -3,16 +3,17 @@ * @package Forward * * @author RapidDev - * @copyright Copyright (c) 2019, RapidDev + * @copyright Copyright (c) 2019-2020, RapidDev * @link https://www.rdev.cc/forward * @license https://opensource.org/licenses/MIT */ namespace Forward; defined('ABSPATH') or die('No script kiddies please!'); - $footer_media = self::home_url().RED_MEDIA; + $this->GetHeader(); + $this->GetNavigation(); +?> +to jest to +GetFooter(); ?> -
- - - \ No newline at end of file diff --git a/app/themes/pages/rdev-home.php b/app/themes/pages/rdev-home.php new file mode 100644 index 0000000..8941a9e --- /dev/null +++ b/app/themes/pages/rdev-home.php @@ -0,0 +1,60 @@ +GetHeader(); +?> +
+ + + + Forward big background image + +
+ +
+
+
+ +
+
+
+
+
+

Forward | _e('Short links manager'); ?>

+

_e('Access to the site requires administrator privileges'); ?>

+
+ +
+ +

Forward | Description(); ?>

+
+
+
+
+
+
+
+ + +GetFooter(); +?> diff --git a/app/themes/pages/rdev-install.php b/app/themes/pages/rdev-install.php new file mode 100644 index 0000000..73f0a76 --- /dev/null +++ b/app/themes/pages/rdev-install.php @@ -0,0 +1,122 @@ +GetHeader(); +?> +
+ + + + Forward big background image + +
+ +
+
+
+ +
+
+
+
+
+ +
+

Quick Install

+ "> + Default URL +
+ +
+ Database +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ + +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+
+ +
+ + +
+
+
+
+ +GetFooter(); +?> diff --git a/app/themes/pages/rdev-login.php b/app/themes/pages/rdev-login.php new file mode 100644 index 0000000..d9577d7 --- /dev/null +++ b/app/themes/pages/rdev-login.php @@ -0,0 +1,74 @@ +GetHeader(); +?> +
+ + + + Forward big background image + +
+ +
+
+
+ +
+
+ +
+
+
+ + +GetFooter(); +?> diff --git a/app/themes/rdev-footer.php b/app/themes/rdev-footer.php new file mode 100644 index 0000000..1ad58b8 --- /dev/null +++ b/app/themes/rdev-footer.php @@ -0,0 +1,19 @@ + + + scripts as $script): ?> + + +Footer(); } ?> + + diff --git a/app/themes/rdev-header.php b/app/themes/rdev-header.php new file mode 100644 index 0000000..9763506 --- /dev/null +++ b/app/themes/rdev-header.php @@ -0,0 +1,56 @@ + + + + + <?php echo $this->Title(); ?> + + + + + + + + + + + + + + + + + + + + +prefetch as $dns): ?> + + +styles as $style): ?> + + + + + + + + + + +Header(); } ?> + + +
diff --git a/app/themes/rdev-navigation.php b/app/themes/rdev-navigation.php new file mode 100644 index 0000000..bd542eb --- /dev/null +++ b/app/themes/rdev-navigation.php @@ -0,0 +1,109 @@ +baseurl . $this->Forward->Options->Get( 'dashboard', 'dashboard' ) . '/'; +?> + + \ No newline at end of file diff --git a/index.php b/index.php index af4af62..c1bd014 100644 --- a/index.php +++ b/index.php @@ -3,21 +3,33 @@ * @package Forward * * @author RapidDev - * @copyright Copyright (c) 2019, RapidDev + * @copyright Copyright (c) 2019-2020, RapidDev * @link https://www.rdev.cc/forward * @license https://opensource.org/licenses/MIT */ namespace Forward; + /** Verify PHP version */ + if (version_compare($ver = PHP_VERSION, $req = '7.2.27', '<')) + exit(sprintf('You are running PHP %s, but Radiograph needs at least PHP %s to run.', $ver, $req)); + + /** Define timezone */ + date_default_timezone_set('UTC'); + + /** Forward version */ + define( 'FORWARD_VERSION', '2.0.0' ); + /** The name of the directory with Forward files */ - define('ADMIN_PATH', 'admin'); + define( 'APP_FOLDER', 'app' ); /** Main constants for all files */ - define('ABSPATH', dirname( __FILE__ ).'/'); - define('ADMPATH', ABSPATH.ADMIN_PATH.'/'); + define( 'ABSPATH', dirname( __FILE__ ) . '/' ); + define( 'APPPATH', ABSPATH . APP_FOLDER . '/' ); /** Initialization file */ - if (!is_file(ADMPATH.'red-init.php')) + if ( !is_file( APPPATH . 'system/rdev-init.php' ) ) exit('Fatal error'); - require_once(ADMPATH.'red-init.php'); -?> \ No newline at end of file + + require_once APPPATH . 'system/rdev-init.php' ; + +?> diff --git a/media/css/bootstrap.min.css b/media/css/bootstrap.min.css deleted file mode 100644 index 92e3fe8..0000000 --- a/media/css/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.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-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/media/css/bootstrap.min.css.map b/media/css/bootstrap.min.css.map deleted file mode 100644 index e69de29..0000000 diff --git a/media/css/chartist.css b/media/css/chartist.css deleted file mode 100644 index bb19a80..0000000 --- a/media/css/chartist.css +++ /dev/null @@ -1,615 +0,0 @@ -.ct-label { - fill: rgba(0, 0, 0, 0.4); - color: rgba(0, 0, 0, 0.4); - font-size: 0.75rem; - line-height: 1; } - -.ct-chart-line .ct-label, -.ct-chart-bar .ct-label { - display: block; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; } - -.ct-chart-pie .ct-label, -.ct-chart-donut .ct-label { - dominant-baseline: central; } - -.ct-label.ct-horizontal.ct-start { - -webkit-box-align: flex-end; - -webkit-align-items: flex-end; - -ms-flex-align: flex-end; - align-items: flex-end; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: start; } - -.ct-label.ct-horizontal.ct-end { - -webkit-box-align: flex-start; - -webkit-align-items: flex-start; - -ms-flex-align: flex-start; - align-items: flex-start; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: start; } - -.ct-label.ct-vertical.ct-start { - -webkit-box-align: flex-end; - -webkit-align-items: flex-end; - -ms-flex-align: flex-end; - align-items: flex-end; - -webkit-box-pack: flex-end; - -webkit-justify-content: flex-end; - -ms-flex-pack: flex-end; - justify-content: flex-end; - text-align: right; - text-anchor: end; } - -.ct-label.ct-vertical.ct-end { - -webkit-box-align: flex-end; - -webkit-align-items: flex-end; - -ms-flex-align: flex-end; - align-items: flex-end; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: start; } - -.ct-chart-bar .ct-label.ct-horizontal.ct-start { - -webkit-box-align: flex-end; - -webkit-align-items: flex-end; - -ms-flex-align: flex-end; - align-items: flex-end; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - text-align: center; - text-anchor: start; } - -.ct-chart-bar .ct-label.ct-horizontal.ct-end { - -webkit-box-align: flex-start; - -webkit-align-items: flex-start; - -ms-flex-align: flex-start; - align-items: flex-start; - -webkit-box-pack: center; - -webkit-justify-content: center; - -ms-flex-pack: center; - justify-content: center; - text-align: center; - text-anchor: start; } - -.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start { - -webkit-box-align: flex-end; - -webkit-align-items: flex-end; - -ms-flex-align: flex-end; - align-items: flex-end; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: start; } - -.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end { - -webkit-box-align: flex-start; - -webkit-align-items: flex-start; - -ms-flex-align: flex-start; - align-items: flex-start; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: start; } - -.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start { - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: flex-end; - -webkit-justify-content: flex-end; - -ms-flex-pack: flex-end; - justify-content: flex-end; - text-align: right; - text-anchor: end; } - -.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end { - -webkit-box-align: center; - -webkit-align-items: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: flex-start; - -webkit-justify-content: flex-start; - -ms-flex-pack: flex-start; - justify-content: flex-start; - text-align: left; - text-anchor: end; } - -.ct-grid { - stroke: rgba(0, 0, 0, 0.2); - stroke-width: 1px; - stroke-dasharray: 2px; } - -.ct-grid-background { - fill: none; } - -.ct-point { - stroke-width: 10px; - stroke-linecap: round; } - -.ct-line { - fill: none; - stroke-width: 4px; } - -.ct-area { - stroke: none; - fill-opacity: 0.1; } - -.ct-bar { - fill: none; - stroke-width: 10px; } - -.ct-slice-donut { - fill: none; - stroke-width: 60px; } - -.ct-series-a .ct-point, .ct-series-a .ct-line, .ct-series-a .ct-bar, .ct-series-a .ct-slice-donut { - stroke: #d70206; } - -.ct-series-a .ct-slice-pie, .ct-series-a .ct-slice-donut-solid, .ct-series-a .ct-area { - fill: #d70206; } - -.ct-series-b .ct-point, .ct-series-b .ct-line, .ct-series-b .ct-bar, .ct-series-b .ct-slice-donut { - stroke: #f05b4f; } - -.ct-series-b .ct-slice-pie, .ct-series-b .ct-slice-donut-solid, .ct-series-b .ct-area { - fill: #f05b4f; } - -.ct-series-c .ct-point, .ct-series-c .ct-line, .ct-series-c .ct-bar, .ct-series-c .ct-slice-donut { - stroke: #f4c63d; } - -.ct-series-c .ct-slice-pie, .ct-series-c .ct-slice-donut-solid, .ct-series-c .ct-area { - fill: #f4c63d; } - -.ct-series-d .ct-point, .ct-series-d .ct-line, .ct-series-d .ct-bar, .ct-series-d .ct-slice-donut { - stroke: #d17905; } - -.ct-series-d .ct-slice-pie, .ct-series-d .ct-slice-donut-solid, .ct-series-d .ct-area { - fill: #d17905; } - -.ct-series-e .ct-point, .ct-series-e .ct-line, .ct-series-e .ct-bar, .ct-series-e .ct-slice-donut { - stroke: #453d3f; } - -.ct-series-e .ct-slice-pie, .ct-series-e .ct-slice-donut-solid, .ct-series-e .ct-area { - fill: #453d3f; } - -.ct-series-f .ct-point, .ct-series-f .ct-line, .ct-series-f .ct-bar, .ct-series-f .ct-slice-donut { - stroke: #59922b; } - -.ct-series-f .ct-slice-pie, .ct-series-f .ct-slice-donut-solid, .ct-series-f .ct-area { - fill: #59922b; } - -.ct-series-g .ct-point, .ct-series-g .ct-line, .ct-series-g .ct-bar, .ct-series-g .ct-slice-donut { - stroke: #0544d3; } - -.ct-series-g .ct-slice-pie, .ct-series-g .ct-slice-donut-solid, .ct-series-g .ct-area { - fill: #0544d3; } - -.ct-series-h .ct-point, .ct-series-h .ct-line, .ct-series-h .ct-bar, .ct-series-h .ct-slice-donut { - stroke: #6b0392; } - -.ct-series-h .ct-slice-pie, .ct-series-h .ct-slice-donut-solid, .ct-series-h .ct-area { - fill: #6b0392; } - -.ct-series-i .ct-point, .ct-series-i .ct-line, .ct-series-i .ct-bar, .ct-series-i .ct-slice-donut { - stroke: #f05b4f; } - -.ct-series-i .ct-slice-pie, .ct-series-i .ct-slice-donut-solid, .ct-series-i .ct-area { - fill: #f05b4f; } - -.ct-series-j .ct-point, .ct-series-j .ct-line, .ct-series-j .ct-bar, .ct-series-j .ct-slice-donut { - stroke: #dda458; } - -.ct-series-j .ct-slice-pie, .ct-series-j .ct-slice-donut-solid, .ct-series-j .ct-area { - fill: #dda458; } - -.ct-series-k .ct-point, .ct-series-k .ct-line, .ct-series-k .ct-bar, .ct-series-k .ct-slice-donut { - stroke: #eacf7d; } - -.ct-series-k .ct-slice-pie, .ct-series-k .ct-slice-donut-solid, .ct-series-k .ct-area { - fill: #eacf7d; } - -.ct-series-l .ct-point, .ct-series-l .ct-line, .ct-series-l .ct-bar, .ct-series-l .ct-slice-donut { - stroke: #86797d; } - -.ct-series-l .ct-slice-pie, .ct-series-l .ct-slice-donut-solid, .ct-series-l .ct-area { - fill: #86797d; } - -.ct-series-m .ct-point, .ct-series-m .ct-line, .ct-series-m .ct-bar, .ct-series-m .ct-slice-donut { - stroke: #b2c326; } - -.ct-series-m .ct-slice-pie, .ct-series-m .ct-slice-donut-solid, .ct-series-m .ct-area { - fill: #b2c326; } - -.ct-series-n .ct-point, .ct-series-n .ct-line, .ct-series-n .ct-bar, .ct-series-n .ct-slice-donut { - stroke: #6188e2; } - -.ct-series-n .ct-slice-pie, .ct-series-n .ct-slice-donut-solid, .ct-series-n .ct-area { - fill: #6188e2; } - -.ct-series-o .ct-point, .ct-series-o .ct-line, .ct-series-o .ct-bar, .ct-series-o .ct-slice-donut { - stroke: #a748ca; } - -.ct-series-o .ct-slice-pie, .ct-series-o .ct-slice-donut-solid, .ct-series-o .ct-area { - fill: #a748ca; } - -.ct-square { - display: block; - position: relative; - width: 100%; } - .ct-square:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 100%; } - .ct-square:after { - content: ""; - display: table; - clear: both; } - .ct-square > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-minor-second { - display: block; - position: relative; - width: 100%; } - .ct-minor-second:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 93.75%; } - .ct-minor-second:after { - content: ""; - display: table; - clear: both; } - .ct-minor-second > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-second { - display: block; - position: relative; - width: 100%; } - .ct-major-second:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 88.8888888889%; } - .ct-major-second:after { - content: ""; - display: table; - clear: both; } - .ct-major-second > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-minor-third { - display: block; - position: relative; - width: 100%; } - .ct-minor-third:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 83.3333333333%; } - .ct-minor-third:after { - content: ""; - display: table; - clear: both; } - .ct-minor-third > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-third { - display: block; - position: relative; - width: 100%; } - .ct-major-third:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 80%; } - .ct-major-third:after { - content: ""; - display: table; - clear: both; } - .ct-major-third > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-perfect-fourth { - display: block; - position: relative; - width: 100%; } - .ct-perfect-fourth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 75%; } - .ct-perfect-fourth:after { - content: ""; - display: table; - clear: both; } - .ct-perfect-fourth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-perfect-fifth { - display: block; - position: relative; - width: 100%; } - .ct-perfect-fifth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 66.6666666667%; } - .ct-perfect-fifth:after { - content: ""; - display: table; - clear: both; } - .ct-perfect-fifth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-minor-sixth { - display: block; - position: relative; - width: 100%; } - .ct-minor-sixth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 62.5%; } - .ct-minor-sixth:after { - content: ""; - display: table; - clear: both; } - .ct-minor-sixth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-golden-section { - display: block; - position: relative; - width: 100%; } - .ct-golden-section:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 61.804697157%; } - .ct-golden-section:after { - content: ""; - display: table; - clear: both; } - .ct-golden-section > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-sixth { - display: block; - position: relative; - width: 100%; } - .ct-major-sixth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 60%; } - .ct-major-sixth:after { - content: ""; - display: table; - clear: both; } - .ct-major-sixth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-minor-seventh { - display: block; - position: relative; - width: 100%; } - .ct-minor-seventh:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 56.25%; } - .ct-minor-seventh:after { - content: ""; - display: table; - clear: both; } - .ct-minor-seventh > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-seventh { - display: block; - position: relative; - width: 100%; } - .ct-major-seventh:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 53.3333333333%; } - .ct-major-seventh:after { - content: ""; - display: table; - clear: both; } - .ct-major-seventh > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-octave { - display: block; - position: relative; - width: 100%; } - .ct-octave:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 50%; } - .ct-octave:after { - content: ""; - display: table; - clear: both; } - .ct-octave > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-tenth { - display: block; - position: relative; - width: 100%; } - .ct-major-tenth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 40%; } - .ct-major-tenth:after { - content: ""; - display: table; - clear: both; } - .ct-major-tenth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-eleventh { - display: block; - position: relative; - width: 100%; } - .ct-major-eleventh:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 37.5%; } - .ct-major-eleventh:after { - content: ""; - display: table; - clear: both; } - .ct-major-eleventh > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-major-twelfth { - display: block; - position: relative; - width: 100%; } - .ct-major-twelfth:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 33.3333333333%; } - .ct-major-twelfth:after { - content: ""; - display: table; - clear: both; } - .ct-major-twelfth > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -.ct-double-octave { - display: block; - position: relative; - width: 100%; } - .ct-double-octave:before { - display: block; - float: left; - content: ""; - width: 0; - height: 0; - padding-bottom: 25%; } - .ct-double-octave:after { - content: ""; - display: table; - clear: both; } - .ct-double-octave > svg { - display: block; - position: absolute; - top: 0; - left: 0; } - -/*# sourceMappingURL=chartist.css.map */ \ No newline at end of file diff --git a/media/css/chartist.css.map b/media/css/chartist.css.map deleted file mode 100644 index 106c076..0000000 --- a/media/css/chartist.css.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "chartist.css", - "sources": [ - "../../src/styles/chartist.scss", - "../../src/styles/settings/_chartist-settings.scss" - ], - "mappings": "AAoHE,AAAA,SAAS,CAAT;EAxDA,IAAI,EC7BU,kBAAI;ED8BlB,KAAK,EC9BS,kBAAI;ED+BlB,SAAS,EC9BI,OAAO;ED+BpB,WAAW,EC5BS,CAAC,GDmFpB;;AAED,AAAe,cAAD,CAAC,SAAS;AACxB,AAAc,aAAD,CAAC,SAAS,CADvB;EArEA,OAAO,EAAE,KAAM;EACf,OAAO,EAAE,WAAY;EACrB,OAAO,EAAE,QAAS;EAClB,OAAO,EAAE,WAAY;EACrB,OAAO,EAAE,YAAa;EACtB,OAAO,EAAE,IAAK,GAkEb;;AAED,AAAc,aAAD,CAAC,SAAS;AACvB,AAAgB,eAAD,CAAC,SAAS,CADzB;EACE,iBAAiB,EAAE,OAAQ,GAC5B;;AAED,AAAuB,SAAd,AAAA,cAAc,AAAA,SAAS,CAAhC;EAjGA,iBAAiB,EAkGW,QAAQ;EAjGpC,mBAAmB,EAiGS,QAAQ;EAhGpC,cAAc,EAgGc,QAAQ;EA/FpC,WAAW,EA+FiB,QAAQ;EA9FpC,gBAAgB,EA8FsB,UAAU;EA7FhD,uBAAuB,EA6Fe,UAAU;EA5FhD,aAAa,EA4FyB,UAAU;EA3FhD,eAAe,EA2FuB,UAAU;EAxF9C,UAAU,EAAE,IAAK;EA0FjB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAuB,SAAd,AAAA,cAAc,AAAA,OAAO,CAA9B;EAvGA,iBAAiB,EAwGW,UAAU;EAvGtC,mBAAmB,EAuGS,UAAU;EAtGtC,cAAc,EAsGc,UAAU;EArGtC,WAAW,EAqGiB,UAAU;EApGtC,gBAAgB,EAoGwB,UAAU;EAnGlD,uBAAuB,EAmGiB,UAAU;EAlGlD,aAAa,EAkG2B,UAAU;EAjGlD,eAAe,EAiGyB,UAAU;EA9FhD,UAAU,EAAE,IAAK;EAgGjB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAqB,SAAZ,AAAA,YAAY,AAAA,SAAS,CAA9B;EA7GA,iBAAiB,EA8GW,QAAQ;EA7GpC,mBAAmB,EA6GS,QAAQ;EA5GpC,cAAc,EA4Gc,QAAQ;EA3GpC,WAAW,EA2GiB,QAAQ;EA1GpC,gBAAgB,EA0GsB,QAAQ;EAzG9C,uBAAuB,EAyGe,QAAQ;EAxG9C,aAAa,EAwGyB,QAAQ;EAvG9C,eAAe,EAuGuB,QAAQ;EAlG5C,UAAU,EAAE,KAAM;EAoGlB,WAAW,EAAE,GAAI,GAClB;;AAED,AAAqB,SAAZ,AAAA,YAAY,AAAA,OAAO,CAA5B;EAnHA,iBAAiB,EAoHW,QAAQ;EAnHpC,mBAAmB,EAmHS,QAAQ;EAlHpC,cAAc,EAkHc,QAAQ;EAjHpC,WAAW,EAiHiB,QAAQ;EAhHpC,gBAAgB,EAgHsB,UAAU;EA/GhD,uBAAuB,EA+Ge,UAAU;EA9GhD,aAAa,EA8GyB,UAAU;EA7GhD,eAAe,EA6GuB,UAAU;EA1G9C,UAAU,EAAE,IAAK;EA4GjB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAqC,aAAxB,CAAC,SAAS,AAAA,cAAc,AAAA,SAAS,CAA9C;EAzHA,iBAAiB,EA0HW,QAAQ;EAzHpC,mBAAmB,EAyHS,QAAQ;EAxHpC,cAAc,EAwHc,QAAQ;EAvHpC,WAAW,EAuHiB,QAAQ;EAtHpC,gBAAgB,EAsHsB,MAAM;EArH5C,uBAAuB,EAqHe,MAAM;EApH5C,aAAa,EAoHyB,MAAM;EAnH5C,eAAe,EAmHuB,MAAM;EA5G1C,UAAU,EAAE,MAAO;EA8GnB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAqC,aAAxB,CAAC,SAAS,AAAA,cAAc,AAAA,OAAO,CAA5C;EA/HA,iBAAiB,EAgIW,UAAU;EA/HtC,mBAAmB,EA+HS,UAAU;EA9HtC,cAAc,EA8Hc,UAAU;EA7HtC,WAAW,EA6HiB,UAAU;EA5HtC,gBAAgB,EA4HwB,MAAM;EA3H9C,uBAAuB,EA2HiB,MAAM;EA1H9C,aAAa,EA0H2B,MAAM;EAzH9C,eAAe,EAyHyB,MAAM;EAlH5C,UAAU,EAAE,MAAO;EAoHnB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAwD,aAA3C,AAAA,mBAAmB,CAAC,SAAS,AAAA,cAAc,AAAA,SAAS,CAAjE;EArIA,iBAAiB,EAsIW,QAAQ;EArIpC,mBAAmB,EAqIS,QAAQ;EApIpC,cAAc,EAoIc,QAAQ;EAnIpC,WAAW,EAmIiB,QAAQ;EAlIpC,gBAAgB,EAkIsB,UAAU;EAjIhD,uBAAuB,EAiIe,UAAU;EAhIhD,aAAa,EAgIyB,UAAU;EA/HhD,eAAe,EA+HuB,UAAU;EA5H9C,UAAU,EAAE,IAAK;EA8HjB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAwD,aAA3C,AAAA,mBAAmB,CAAC,SAAS,AAAA,cAAc,AAAA,OAAO,CAA/D;EA3IA,iBAAiB,EA4IW,UAAU;EA3ItC,mBAAmB,EA2IS,UAAU;EA1ItC,cAAc,EA0Ic,UAAU;EAzItC,WAAW,EAyIiB,UAAU;EAxItC,gBAAgB,EAwIwB,UAAU;EAvIlD,uBAAuB,EAuIiB,UAAU;EAtIlD,aAAa,EAsI2B,UAAU;EArIlD,eAAe,EAqIyB,UAAU;EAlIhD,UAAU,EAAE,IAAK;EAoIjB,WAAW,EAAE,KAAM,GACpB;;AAED,AAAsD,aAAzC,AAAA,mBAAmB,CAAC,SAAS,AAAA,YAAY,AAAA,SAAS,CAA/D;EAjJA,iBAAiB,EAmJW,MAAM;EAlJlC,mBAAmB,EAkJS,MAAM;EAjJlC,cAAc,EAiJc,MAAM;EAhJlC,WAAW,EAgJiB,MAAM;EA/IlC,gBAAgB,EA+IoB,QAAQ;EA9I5C,uBAAuB,EA8Ia,QAAQ;EA7I5C,aAAa,EA6IuB,QAAQ;EA5I5C,eAAe,EA4IqB,QAAQ;EAvI1C,UAAU,EAAE,KAAM;EAyIlB,WAAW,EAAE,GAAI,GAClB;;AAED,AAAsD,aAAzC,AAAA,mBAAmB,CAAC,SAAS,AAAA,YAAY,AAAA,OAAO,CAA7D;EAxJA,iBAAiB,EAyJW,MAAM;EAxJlC,mBAAmB,EAwJS,MAAM;EAvJlC,cAAc,EAuJc,MAAM;EAtJlC,WAAW,EAsJiB,MAAM;EArJlC,gBAAgB,EAqJoB,UAAU;EApJ9C,uBAAuB,EAoJa,UAAU;EAnJ9C,aAAa,EAmJuB,UAAU;EAlJ9C,eAAe,EAkJqB,UAAU;EA/I5C,UAAU,EAAE,IAAK;EAiJjB,WAAW,EAAE,GAAI,GAClB;;AAED,AAAA,QAAQ,CAAR;EA1HA,MAAM,EC7BQ,kBAAI;ED8BlB,YAAY,EC5BE,GAAG;ED+Bf,gBAAgB,EChCA,GAAG,GDwJpB;;AAED,AAAA,mBAAmB,CAAnB;EACE,IAAI,ECzJkB,IAAI,GD0J3B;;AAED,AAAA,SAAS,CAAT;EAzHA,YAAY,EC9BE,IAAI;ED+BlB,cAAc,EC7BC,KAAK,GDuJnB;;AAED,AAAA,QAAQ,CAAR;EAxHA,IAAI,EAAE,IAAK;EACX,YAAY,ECtCE,GAAG,GD+JhB;;AAED,AAAA,QAAQ,CAAR;EAnHA,MAAM,EAAE,IAAK;EACb,YAAY,ECzCI,GAAG,GD6JlB;;AAED,AAAA,OAAO,CAAP;EAlHA,IAAI,EAAE,IAAK;EACX,YAAY,EC3CC,IAAI,GD8JhB;;AAED,AAAA,eAAe,CAAf;EAjHA,IAAI,EAAE,IAAK;EACX,YAAY,EC7CG,IAAI,GD+JlB;;AAIG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EClCR,OAAO,GDmCN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECtCN,OAAO,GDuCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECjCR,OAAO,GDkCN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECrCN,OAAO,GDsCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EChCR,OAAO,GDiCN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECpCN,OAAO,GDqCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC/BR,OAAO,GDgCN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECnCN,OAAO,GDoCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC9BR,OAAO,GD+BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EClCN,OAAO,GDmCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC7BR,OAAO,GD8BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECjCN,OAAO,GDkCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC5BR,OAAO,GD6BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EChCN,OAAO,GDiCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC3BR,OAAO,GD4BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC/BN,OAAO,GDgCN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,EC1BR,OAAO,GD2BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC9BN,OAAO,GD+BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECzBR,OAAO,GD0BN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC7BN,OAAO,GD8BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECxBR,OAAO,GDyBN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC5BN,OAAO,GD6BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECvBR,OAAO,GDwBN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC3BN,OAAO,GD4BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECtBR,OAAO,GDuBN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,EC1BN,OAAO,GD2BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECrBR,OAAO,GDsBN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECzBN,OAAO,GD0BN;;AA4GG,AAlHJ,YAkHgB,CAlHhB,SAAS,EAkHL,AAlHO,YAkHK,CAlHL,QAAQ,EAkHf,AAlHiB,YAkHL,CAlHK,OAAO,EAkHxB,AAlH0B,YAkHd,CAlHc,eAAe,CAA7C;EACE,MAAM,ECpBR,OAAO,GDqBN;;AAgHG,AA9GJ,YA8GgB,CA9GhB,aAAa,EA8GT,AA9GW,YA8GC,CA9GD,qBAAqB,EA8GhC,AA9GkC,YA8GtB,CA9GsB,QAAQ,CAA9C;EACE,IAAI,ECxBN,OAAO,GDyBN;;AA0HG,AAAA,UAAU,CAAV;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,UAAU,AAlOb,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,IAAM,GACvB;EA2NG,AAAA,UAAU,AAzNb,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,UAmNY,GAnNZ,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,gBAAgB,CAAhB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,gBAAgB,AAlOnB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,MAAM,GACvB;EA2NG,AAAA,gBAAgB,AAzNnB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,gBAmNkB,GAnNlB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,gBAAgB,CAAhB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,gBAAgB,AAlOnB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,cAAM,GACvB;EA2NG,AAAA,gBAAgB,AAzNnB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,gBAmNkB,GAnNlB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,eAAe,CAAf;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,eAAe,AAlOlB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,cAAM,GACvB;EA2NG,AAAA,eAAe,AAzNlB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,eAmNiB,GAnNjB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,eAAe,CAAf;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,eAAe,AAlOlB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,eAAe,AAzNlB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,eAmNiB,GAnNjB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,kBAAkB,CAAlB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,kBAAkB,AAlOrB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,kBAAkB,AAzNrB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,kBAmNoB,GAnNpB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,iBAAiB,CAAjB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,iBAAiB,AAlOpB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,cAAM,GACvB;EA2NG,AAAA,iBAAiB,AAzNpB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,iBAmNmB,GAnNnB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,eAAe,CAAf;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,eAAe,AAlOlB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,KAAM,GACvB;EA2NG,AAAA,eAAe,AAzNlB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,eAmNiB,GAnNjB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,kBAAkB,CAAlB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,kBAAkB,AAlOrB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,aAAM,GACvB;EA2NG,AAAA,kBAAkB,AAzNrB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,kBAmNoB,GAnNpB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,eAAe,CAAf;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,eAAe,AAlOlB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,eAAe,AAzNlB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,eAmNiB,GAnNjB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,iBAAiB,CAAjB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,iBAAiB,AAlOpB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,MAAM,GACvB;EA2NG,AAAA,iBAAiB,AAzNpB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,iBAmNmB,GAnNnB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,iBAAiB,CAAjB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,iBAAiB,AAlOpB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,cAAM,GACvB;EA2NG,AAAA,iBAAiB,AAzNpB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,iBAmNmB,GAnNnB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,UAAU,CAAV;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,UAAU,AAlOb,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,UAAU,AAzNb,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,UAmNY,GAnNZ,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,eAAe,CAAf;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,eAAe,AAlOlB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,eAAe,AAzNlB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,eAmNiB,GAnNjB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,kBAAkB,CAAlB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,kBAAkB,AAlOrB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,KAAM,GACvB;EA2NG,AAAA,kBAAkB,AAzNrB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,kBAmNoB,GAnNpB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,iBAAiB,CAAjB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,iBAAiB,AAlOpB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,cAAM,GACvB;EA2NG,AAAA,iBAAiB,AAzNpB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,iBAmNmB,GAnNnB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT;;AA8MG,AAAA,iBAAiB,CAAjB;EAtOJ,OAAO,EAAE,KAAM;EACf,QAAQ,EAAE,QAAS;EACnB,KAAK,EAHoC,IAAI,GAyOxC;EAFD,AAAA,iBAAiB,AAlOpB,OAAO,CAAC;IACP,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK;IACZ,OAAO,EAAE,EAAG;IACZ,KAAK,EAAE,CAAE;IACT,MAAM,EAAE,CAAE;IACV,cAAc,EAAE,GAAM,GACvB;EA2NG,AAAA,iBAAiB,AAzNpB,MAAM,CAAC;IACN,OAAO,EAAE,EAAG;IACZ,OAAO,EAAE,KAAM;IACf,KAAK,EAAE,IAAK,GACb;EAqNG,AAnNF,iBAmNmB,GAnNnB,GAAG,CAAC;IACJ,OAAO,EAAE,KAAM;IACf,QAAQ,EAAE,QAAS;IACnB,GAAG,EAAE,CAAE;IACP,IAAI,EAAE,CAAE,GACT", - "names": [] -} \ No newline at end of file diff --git a/media/css/forward.css b/media/css/forward.css new file mode 100644 index 0000000..01dee0d --- /dev/null +++ b/media/css/forward.css @@ -0,0 +1,225 @@ +/*! + * Forward 2.0.0 (https://github.com/rapiddev/Forward) + * Copyright 2018-2020 RapidDev + * Licensed under MIT (https://github.com/rapiddev/Forward/blob/master/LICENSE) + */ + +html { + height: 100%; + box-sizing: border-box; +} + +body { + display: initial; + margin: 0; + min-height: 100%; +} + +#forward { + min-height: 100%; +} + +.page-install > #forward, +.page-home > #forward, +.page-login > #forward { + display: flex; + justify-content: center; + align-items: center; +} + +#big-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +#big-background img { + z-index: -1; + position: fixed; + width: 100%; + min-height: 100%; + filter: brightness(50%); + object-fit: cover; +} + +.navbar-brand > picture { + display: flex; + justify-content: center; + align-items: center; +} + +.navbar-brand > picture > img { + max-width: 100%; + width: 140px; +} + +.card-button { + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; +} + +.forward-logo img { + width: 100%; + max-width: 300px; + margin-top: 15px; + margin-bottom: 15px; +} + +.home-card h1, .login-card h1 { + font-size: 20px; + font-weight: 700; +} + +.home-card .card, .login-card .card, #add-new-card .card { + min-width: 100%; + border: none !important; +} + +.home-card a, .login-card button, #add-new-card button { + border-radius: 0 !important; + border-bottom-right-radius: .25rem !important; + border-bottom-left-radius: .25rem !important; +} + +.splash-footer { + position: absolute; + bottom: 0; + left: 0; + margin: 0; + padding: 10px; + width: 100%; + color: #aaa; + font-size: 10px; + font-weight: 200; +} + +.github { + display: flex; + color: #fff; + justify-content: center; + align-items: center; + font-weight: 200; + font-size: 15px; +} + +.github svg { + margin-right: 10px; +} + +.github p { + margin: 0; +} + +.input-group-addon { + display: flex; + justify-content: center; + align-items: center; + padding: .5rem .75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.25; + color: #495057; + text-align: center; + background-color: #e9ecef; + border: 1px solid rgba(0,0,0,.15); + border-radius: .25rem; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-addon > a { + display: flex; + color: #495057; +} + +.navbar { + padding-bottom: 20px; + padding-top: 20px; +} + +.navbar > div { + padding-right: 2rem; + padding-left: 2rem; +} + +@media(min-width: 992px) +{ + +} + +@media(max-width: 991px) +{ + .navbar-separator { + display: none !important; + } +} + +.navbar-separator > div { + margin-left: 10px; + margin-right: 20px; + height: 100%; + border-left: 1px solid rgba(255,255,255,.55); +} + +.btn-create-new { + height: 100%; + border-radius: 0px; +} + +.input-search-records { + border-radius: 0px; + height: 100%; + background: transparent; + color: rgba(255,255,255,.55); +} + +.user-dropdown { + border: 1px solid rgba(255,255,255,.55); + background: transparent; + max-width: 100%; + width: 200px; + line-height: 13px; + height: 100%; +} + +.user-dropdown > .dropdown-toggle { + display: inline-block; + height: 100%; + width: 100%; + margin: 0 !important; + padding: 0 !important; +} + +.user-dropdown > .dropdown-toggle > div { + display: flex; + width: calc(100% - 15px); + float: left; + padding: 5px 10px; +} + +.user-dropdown > .dropdown-toggle > div > div { + display: flex; + justify-content: center; + align-items: center; +} + +.user-dropdown svg { + margin-right: 10px; +} + +.user-dropdown small { + font-size: 10px; +} + +.user-dropdown > .dropdown-toggle::after { + vertical-align: 0; + margin: auto; + margin-top: 16px; +} + +.user-dropdown > .dropdown-menu.show { + min-width: 200px !important; +} \ No newline at end of file diff --git a/media/css/red.css b/media/css/red.css deleted file mode 100644 index f4b59d4..0000000 --- a/media/css/red.css +++ /dev/null @@ -1,396 +0,0 @@ -html { - height: 100%; - box-sizing: border-box; -} - -body { - position: relative; - display: initial; - margin: 0; - min-height: 100%; -} - -#main-nav { - position: fixed; - width: 100%; - z-index: 10; -} - -#forward { - min-height: 100%; -} - -.remove-record { - display: flex; - justify-content: flex-end; - align-items: center; -} - -#delete-record-icon > svg { - border: 1px solid rgba(0,0,0,.4); - padding: 5px; - fill: rgba(0,0,0,.4); - border-radius: 150px; - width: 40px; - height: 40px; - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -#delete-record-icon:hover > svg { - border-color: rgba(0,0,0,.8); - fill: rgba(0,0,0,.8); -} - -.red-chart.ct-chart { - background-color: transparent; -} - -.red-chart.ct-chart .ct-bar { - stroke: orange !important; - stroke-width: 20px !important; -} - -.user-card { - margin-bottom: 30px; -} - -.user-card > .btn-group >.btn:not(:first-child) { - border-top-right-radius: 0 !important; -} - -.user-card > .btn-group >.btn:not(:last-child) { - border-top-left-radius: 0 !important; -} - -.user-card p, .user-card h2 { - margin: 0; - padding: 0; -} - -.user-card h2 { - font-weight: 400; -} - -.block-page { - position: fixed; - display: inherit; - top: 0; - left: 0; - right: 0; - bottom: 0; - height: 100vh; - width: 100vw; -} - -.block-page > .container-fluid, .block-page > .container, .block-page > .container > .row, .block-page > .container-fluid > .row { - height: 100%; -} - -hr { - border-top: 2px solid rgba(0,0,0,.1) !important; -} - -.distance-navbar { - padding-top: 56px; -} - -.nav-link { - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -.nav-link > svg { - fill: rgba(255,255,255,.5); - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -.nav-link:hover > svg, .nav-item.active > .nav-link > svg { - fill: rgba(255,255,255,.75); -} - -.uppercase { - text-transform: uppercase; -} - -#red-dashboard { - background-color: #f8f9fa; -} - -#red-about { - min-height: 100%; - padding-bottom: 30px; -} - -#users-table { - margin-top: 35px; -} - -#red-about, #red-users { - padding-top: 96px; -} - -#dashboard-stats { - background-color: #2b3e4b; - padding-top: 15px; - padding-bottom: 15px; - color: #fff; -} - -#dashboard-content { - position: fixed; - display: inherit; - height: 100%; - width: 100%; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin-top: 56px; - background-color: #f8f9fa; -} - -#links-table { - margin-top: 25px; -} - -#forward-logo img { - width: 100%; - max-width: 300px; - margin-top: 15px; - margin-bottom: 15px; -} - -#home-footer { - position: absolute; - bottom: 0; - padding: 10px; - width: 100%; - color: #eee; - font-size: 10px; - font-weight: 200; -} - -#forward-navbar-logo { - display: flex; - align-items: center; -} -#forward-navbar-logo img { - width: 100%; - max-width: 110px; -} - -#github { - display: flex; - color: #fff; - justify-content: center; - align-items: center; - font-weight: 200; - font-size: 15px; -} - -#github svg { - margin-right: 10px; -} - -#github p { - margin: 0; -} - -#red-home, #red-login, #red-install { - position: fixed; - top: 0; - bottom: 0; - width: 100%; - display: flex; - align-items: center; -} - -#red-settings, #red-users { - padding-top: 86px; - padding-bottom: 32px; -} - -#settings-form { - margin-top: 32px; -} - -#big-background img { - position: fixed; - width: 100%; - min-height: 100%; - filter: brightness(50%); - object-fit: cover; -} - -#home-card h1, #login-card h1 { - font-size: 20px; - font-weight: 700; -} - -#home-card .card, #login-card .card, #add-new-card .card { - min-width: 100%; - border: none !important; -} - -#home-card a, #login-card button, #add-new-card button { - border-radius: 0 !important; - border-bottom-right-radius: .25rem !important; - border-bottom-left-radius: .25rem !important; -} - -#records_list { - background-color: #fff; - overflow: auto; - height: inherit; -} - -#records_list::-webkit-scrollbar, #dashboard-box::-webkit-scrollbar { - width: 2px; - background-color: transparent; -} - -#records_list::-webkit-scrollbar-track, #dashboard-box::-webkit-scrollbar-track { - background-color: transparent; -} - -#records_list::-webkit-scrollbar-thumb, #dashboard-box::-webkit-scrollbar-thumb { - background-color: rgba(0,0,0,.125); -} - -.links-card, .links-header { - cursor: pointer; - border-radius: 0 !important; - -webkit-transition: all 0.2s ease-in-out; - -moz-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -.links-card:hover, .links-header:hover { - background: #f8f9fa; -} - -.links-card p, .links-card h2, .links-header p { - margin: 0; - padding: 0; -} - -.links-card h2 { - font-size: 22px; - font-weight: 700; -} - -.links-card p, .links-header p { - font-size: 12px; -} - -.links-card h2 > a { - color: #333; -} - - -.links-card p > a, #preview-record-url { - color: orange; -} - -.links-card > .card-body, .links-header > .card-body { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.75rem !important; -} - -.links-card > .card-body > span { - font-weight: 700; - font-size: 25px; -} - -.btn-icon { - padding: 1px 4px !important; - margin-left: 10px; -} - -.td-buttons { - display: flex; - align-items: center; - justify-content: flex-end; -} - -#github-about a { - display: flex; - align-items: center; - color: #000; -} - -#github-about svg { - margin-right: 10px; -} - -#github-about p { - margin: 0; -} - -.stats-header { - display: flex; - align-items: center; - margin-bottom: 10px; - margin-top: 10px; -} - -.stats-header svg { - width: 40px; - height: 40px; - border: 1px solid #444; - border-radius: 30px; - padding: 5px; - fill: #444; - margin-right: 15px; -} - -.stats-header h1 { - font-size: 20px; - margin: 0; -} - -.stats-header p { - margin: 0; - opacity: .55; - font-size: 11px; -} - -.navbar-right { - justify-content: flex-end; -} - -.col-no-gutters { - padding-left: 0 !important; - padding-right: 0 !important; -} - -.p-warning { - margin: 0; - color: #eb4334; -} - -.p-warning > small { - font-weight: bold; -} - - -@media(max-width: 991px) -{ - #records_list { - height: 300px; - } - .red-chart.ct-chart .ct-bar { - stroke-width: 10px !important; - } -} \ No newline at end of file diff --git a/media/img/forward-fav-192.png b/media/img/forward-fav-192.png new file mode 100644 index 0000000..52964ef Binary files /dev/null and b/media/img/forward-fav-192.png differ diff --git a/media/img/forward-fav.png b/media/img/forward-fav-256.png similarity index 100% rename from media/img/forward-fav.png rename to media/img/forward-fav-256.png diff --git a/media/img/forward-fav-32.png b/media/img/forward-fav-32.png new file mode 100644 index 0000000..9734807 Binary files /dev/null and b/media/img/forward-fav-32.png differ diff --git a/media/img/forward-fav-64.png b/media/img/forward-fav-64.png new file mode 100644 index 0000000..7398b21 Binary files /dev/null and b/media/img/forward-fav-64.png differ diff --git a/media/js/bootstrap.min.js b/media/js/bootstrap.min.js deleted file mode 100644 index c4c0d1f..0000000 --- a/media/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Ee},je="show",He="out",Re={HIDE:"hide"+De,HIDDEN:"hidden"+De,SHOW:"show"+De,SHOWN:"shown"+De,INSERTED:"inserted"+De,CLICK:"click"+De,FOCUSIN:"focusin"+De,FOCUSOUT:"focusout"+De,MOUSEENTER:"mouseenter"+De,MOUSELEAVE:"mouseleave"+De},xe="fade",Fe="show",Ue=".tooltip-inner",We=".arrow",qe="hover",Me="focus",Ke="click",Qe="manual",Be=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(xe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:We},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===He&&e._leave(null,e)};if(g(this.tip).hasClass(xe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==je&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ke]=!1,this._activeTrigger[Me]=!1,this._activeTrigger[qe]=!1,g(this.tip).hasClass(xe)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ae+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ue)),this.getTitle()),g(t).removeClass(xe+" "+Fe)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Se(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Pe[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Qe){var e=t===qe?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===qe?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Me:qe]=!0),g(e.getTipElement()).hasClass(Fe)||e._hoverState===je?e._hoverState=je:(clearTimeout(e._timeout),e._hoverState=je,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===je&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Me:qe]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===He&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==Oe.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(be,t,this.constructor.DefaultType),t.sanitize&&(t.template=Se(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ne);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(xe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ie),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ie,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return Le}},{key:"NAME",get:function(){return be}},{key:"DATA_KEY",get:function(){return Ie}},{key:"Event",get:function(){return Re}},{key:"EVENT_KEY",get:function(){return De}},{key:"DefaultType",get:function(){return ke}}]),i}();g.fn[be]=Be._jQueryInterface,g.fn[be].Constructor=Be,g.fn[be].noConflict=function(){return g.fn[be]=we,Be._jQueryInterface};var Ve="popover",Ye="bs.popover",ze="."+Ye,Xe=g.fn[Ve],$e="bs-popover",Ge=new RegExp("(^|\\s)"+$e+"\\S+","g"),Je=l({},Be.Default,{placement:"right",trigger:"click",content:"",template:''}),Ze=l({},Be.DefaultType,{content:"(string|element|function)"}),tn="fade",en="show",nn=".popover-header",on=".popover-body",rn={HIDE:"hide"+ze,HIDDEN:"hidden"+ze,SHOW:"show"+ze,SHOWN:"shown"+ze,INSERTED:"inserted"+ze,CLICK:"click"+ze,FOCUSIN:"focusin"+ze,FOCUSOUT:"focusout"+ze,MOUSEENTER:"mouseenter"+ze,MOUSELEAVE:"mouseleave"+ze},sn=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass($e+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(nn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(on),e),t.removeClass(tn+" "+en)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ge);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t {\n called = true\n })\n\n setTimeout(() => {\n if (!called) {\n Util.triggerTransitionEnd(this)\n }\n }, duration)\n\n return this\n}\n\nfunction setTransitionEndSupport() {\n $.fn.emulateTransitionEnd = transitionEndEmulator\n $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst Util = {\n\n TRANSITION_END: 'bsTransitionEnd',\n\n getUID(prefix) {\n do {\n // eslint-disable-next-line no-bitwise\n prefix += ~~(Math.random() * MAX_UID) // \"~~\" acts like a faster Math.floor() here\n } while (document.getElementById(prefix))\n return prefix\n },\n\n getSelectorFromElement(element) {\n let selector = element.getAttribute('data-target')\n\n if (!selector || selector === '#') {\n const hrefAttr = element.getAttribute('href')\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''\n }\n\n try {\n return document.querySelector(selector) ? selector : null\n } catch (err) {\n return null\n }\n },\n\n getTransitionDurationFromElement(element) {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let transitionDuration = $(element).css('transition-duration')\n let transitionDelay = $(element).css('transition-delay')\n\n const floatTransitionDuration = parseFloat(transitionDuration)\n const floatTransitionDelay = parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n },\n\n reflow(element) {\n return element.offsetHeight\n },\n\n triggerTransitionEnd(element) {\n $(element).trigger(TRANSITION_END)\n },\n\n // TODO: Remove in v5\n supportsTransitionEnd() {\n return Boolean(TRANSITION_END)\n },\n\n isElement(obj) {\n return (obj[0] || obj).nodeType\n },\n\n typeCheckConfig(componentName, config, configTypes) {\n for (const property in configTypes) {\n if (Object.prototype.hasOwnProperty.call(configTypes, property)) {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && Util.isElement(value)\n ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new Error(\n `${componentName.toUpperCase()}: ` +\n `Option \"${property}\" provided type \"${valueType}\" ` +\n `but expected type \"${expectedTypes}\".`)\n }\n }\n }\n },\n\n findShadowRoot(element) {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return Util.findShadowRoot(element.parentNode)\n }\n}\n\nsetTransitionEndSupport()\n\nexport default Util\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Selector = {\n DISMISS : '[data-dismiss=\"alert\"]'\n}\n\nconst Event = {\n CLOSE : `close${EVENT_KEY}`,\n CLOSED : `closed${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n ALERT : 'alert',\n FADE : 'fade',\n SHOW : 'show'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n close(element) {\n let rootElement = this._element\n if (element) {\n rootElement = this._getRootElement(element)\n }\n\n const customEvent = this._triggerCloseEvent(rootElement)\n\n if (customEvent.isDefaultPrevented()) {\n return\n }\n\n this._removeElement(rootElement)\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Private\n\n _getRootElement(element) {\n const selector = Util.getSelectorFromElement(element)\n let parent = false\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n if (!parent) {\n parent = $(element).closest(`.${ClassName.ALERT}`)[0]\n }\n\n return parent\n }\n\n _triggerCloseEvent(element) {\n const closeEvent = $.Event(Event.CLOSE)\n\n $(element).trigger(closeEvent)\n return closeEvent\n }\n\n _removeElement(element) {\n $(element).removeClass(ClassName.SHOW)\n\n if (!$(element).hasClass(ClassName.FADE)) {\n this._destroyElement(element)\n return\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(element)\n\n $(element)\n .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))\n .emulateTransitionEnd(transitionDuration)\n }\n\n _destroyElement(element) {\n $(element)\n .detach()\n .trigger(Event.CLOSED)\n .remove()\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $element = $(this)\n let data = $element.data(DATA_KEY)\n\n if (!data) {\n data = new Alert(this)\n $element.data(DATA_KEY, data)\n }\n\n if (config === 'close') {\n data[config](this)\n }\n })\n }\n\n static _handleDismiss(alertInstance) {\n return function (event) {\n if (event) {\n event.preventDefault()\n }\n\n alertInstance.close(this)\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(\n Event.CLICK_DATA_API,\n Selector.DISMISS,\n Alert._handleDismiss(new Alert())\n)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Alert._jQueryInterface\n$.fn[NAME].Constructor = Alert\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Alert._jQueryInterface\n}\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'button'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst ClassName = {\n ACTIVE : 'active',\n BUTTON : 'btn',\n FOCUS : 'focus'\n}\n\nconst Selector = {\n DATA_TOGGLE_CARROT : '[data-toggle^=\"button\"]',\n DATA_TOGGLE : '[data-toggle=\"buttons\"]',\n INPUT : 'input:not([type=\"hidden\"])',\n ACTIVE : '.active',\n BUTTON : '.btn'\n}\n\nconst Event = {\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +\n `blur${EVENT_KEY}${DATA_API_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n toggle() {\n let triggerChangeEvent = true\n let addAriaPressed = true\n const rootElement = $(this._element).closest(\n Selector.DATA_TOGGLE\n )[0]\n\n if (rootElement) {\n const input = this._element.querySelector(Selector.INPUT)\n\n if (input) {\n if (input.type === 'radio') {\n if (input.checked &&\n this._element.classList.contains(ClassName.ACTIVE)) {\n triggerChangeEvent = false\n } else {\n const activeElement = rootElement.querySelector(Selector.ACTIVE)\n\n if (activeElement) {\n $(activeElement).removeClass(ClassName.ACTIVE)\n }\n }\n }\n\n if (triggerChangeEvent) {\n if (input.hasAttribute('disabled') ||\n rootElement.hasAttribute('disabled') ||\n input.classList.contains('disabled') ||\n rootElement.classList.contains('disabled')) {\n return\n }\n input.checked = !this._element.classList.contains(ClassName.ACTIVE)\n $(input).trigger('change')\n }\n\n input.focus()\n addAriaPressed = false\n }\n }\n\n if (addAriaPressed) {\n this._element.setAttribute('aria-pressed',\n !this._element.classList.contains(ClassName.ACTIVE))\n }\n\n if (triggerChangeEvent) {\n $(this._element).toggleClass(ClassName.ACTIVE)\n }\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n\n if (!data) {\n data = new Button(this)\n $(this).data(DATA_KEY, data)\n }\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n event.preventDefault()\n\n let button = event.target\n\n if (!$(button).hasClass(ClassName.BUTTON)) {\n button = $(button).closest(Selector.BUTTON)\n }\n\n Button._jQueryInterface.call($(button), 'toggle')\n })\n .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n const button = $(event.target).closest(Selector.BUTTON)[0]\n $(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Button._jQueryInterface\n$.fn[NAME].Constructor = Button\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Button._jQueryInterface\n}\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key\nconst ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval : 5000,\n keyboard : true,\n slide : false,\n pause : 'hover',\n wrap : true,\n touch : true\n}\n\nconst DefaultType = {\n interval : '(number|boolean)',\n keyboard : 'boolean',\n slide : '(boolean|string)',\n pause : '(string|boolean)',\n wrap : 'boolean',\n touch : 'boolean'\n}\n\nconst Direction = {\n NEXT : 'next',\n PREV : 'prev',\n LEFT : 'left',\n RIGHT : 'right'\n}\n\nconst Event = {\n SLIDE : `slide${EVENT_KEY}`,\n SLID : `slid${EVENT_KEY}`,\n KEYDOWN : `keydown${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`,\n TOUCHSTART : `touchstart${EVENT_KEY}`,\n TOUCHMOVE : `touchmove${EVENT_KEY}`,\n TOUCHEND : `touchend${EVENT_KEY}`,\n POINTERDOWN : `pointerdown${EVENT_KEY}`,\n POINTERUP : `pointerup${EVENT_KEY}`,\n DRAG_START : `dragstart${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n CAROUSEL : 'carousel',\n ACTIVE : 'active',\n SLIDE : 'slide',\n RIGHT : 'carousel-item-right',\n LEFT : 'carousel-item-left',\n NEXT : 'carousel-item-next',\n PREV : 'carousel-item-prev',\n ITEM : 'carousel-item',\n POINTER_EVENT : 'pointer-event'\n}\n\nconst Selector = {\n ACTIVE : '.active',\n ACTIVE_ITEM : '.active.carousel-item',\n ITEM : '.carousel-item',\n ITEM_IMG : '.carousel-item img',\n NEXT_PREV : '.carousel-item-next, .carousel-item-prev',\n INDICATORS : '.carousel-indicators',\n DATA_SLIDE : '[data-slide], [data-slide-to]',\n DATA_RIDE : '[data-ride=\"carousel\"]'\n}\n\nconst PointerType = {\n TOUCH : 'touch',\n PEN : 'pen'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel {\n constructor(element, config) {\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._element = element\n this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n next() {\n if (!this._isSliding) {\n this._slide(Direction.NEXT)\n }\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden &&\n ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {\n this.next()\n }\n }\n\n prev() {\n if (!this._isSliding) {\n this._slide(Direction.PREV)\n }\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (this._element.querySelector(Selector.NEXT_PREV)) {\n Util.triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config.interval && !this._isPaused) {\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n $(this._element).one(Event.SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const direction = index > activeIndex\n ? Direction.NEXT\n : Direction.PREV\n\n this._slide(direction, this._items[index])\n }\n\n dispose() {\n $(this._element).off(EVENT_KEY)\n $.removeData(this._element, DATA_KEY)\n\n this._items = null\n this._config = null\n this._element = null\n this._interval = null\n this._isPaused = null\n this._isSliding = null\n this._activeElement = null\n this._indicatorsElement = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n // swipe left\n if (direction > 0) {\n this.prev()\n }\n\n // swipe right\n if (direction < 0) {\n this.next()\n }\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n $(this._element)\n .on(Event.KEYDOWN, (event) => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n $(this._element)\n .on(Event.MOUSEENTER, (event) => this.pause(event))\n .on(Event.MOUSELEAVE, (event) => this.cycle(event))\n }\n\n if (this._config.touch) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n if (!this._touchSupported) {\n return\n }\n\n const start = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchStartX = event.originalEvent.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.originalEvent.touches[0].clientX\n }\n }\n\n const move = (event) => {\n // ensure swiping with one touch and not pinching\n if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {\n this.touchDeltaX = 0\n } else {\n this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX\n }\n }\n\n const end = (event) => {\n if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {\n this.touchDeltaX = event.originalEvent.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n $(this._element.querySelectorAll(Selector.ITEM_IMG)).on(Event.DRAG_START, (e) => e.preventDefault())\n if (this._pointerEvent) {\n $(this._element).on(Event.POINTERDOWN, (event) => start(event))\n $(this._element).on(Event.POINTERUP, (event) => end(event))\n\n this._element.classList.add(ClassName.POINTER_EVENT)\n } else {\n $(this._element).on(Event.TOUCHSTART, (event) => start(event))\n $(this._element).on(Event.TOUCHMOVE, (event) => move(event))\n $(this._element).on(Event.TOUCHEND, (event) => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n switch (event.which) {\n case ARROW_LEFT_KEYCODE:\n event.preventDefault()\n this.prev()\n break\n case ARROW_RIGHT_KEYCODE:\n event.preventDefault()\n this.next()\n break\n default:\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode\n ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))\n : []\n return this._items.indexOf(element)\n }\n\n _getItemByDirection(direction, activeElement) {\n const isNextDirection = direction === Direction.NEXT\n const isPrevDirection = direction === Direction.PREV\n const activeIndex = this._getItemIndex(activeElement)\n const lastItemIndex = this._items.length - 1\n const isGoingToWrap = isPrevDirection && activeIndex === 0 ||\n isNextDirection && activeIndex === lastItemIndex\n\n if (isGoingToWrap && !this._config.wrap) {\n return activeElement\n }\n\n const delta = direction === Direction.PREV ? -1 : 1\n const itemIndex = (activeIndex + delta) % this._items.length\n\n return itemIndex === -1\n ? this._items[this._items.length - 1] : this._items[itemIndex]\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))\n const slideEvent = $.Event(Event.SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n\n $(this._element).trigger(slideEvent)\n\n return slideEvent\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))\n $(indicators)\n .removeClass(ClassName.ACTIVE)\n\n const nextIndicator = this._indicatorsElement.children[\n this._getItemIndex(element)\n ]\n\n if (nextIndicator) {\n $(nextIndicator).addClass(ClassName.ACTIVE)\n }\n }\n }\n\n _slide(direction, element) {\n const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || activeElement &&\n this._getItemByDirection(direction, activeElement)\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n let directionalClassName\n let orderClassName\n let eventDirectionName\n\n if (direction === Direction.NEXT) {\n directionalClassName = ClassName.LEFT\n orderClassName = ClassName.NEXT\n eventDirectionName = Direction.LEFT\n } else {\n directionalClassName = ClassName.RIGHT\n orderClassName = ClassName.PREV\n eventDirectionName = Direction.RIGHT\n }\n\n if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {\n this._isSliding = false\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.isDefaultPrevented()) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n\n const slidEvent = $.Event(Event.SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n\n if ($(this._element).hasClass(ClassName.SLIDE)) {\n $(nextElement).addClass(orderClassName)\n\n Util.reflow(nextElement)\n\n $(activeElement).addClass(directionalClassName)\n $(nextElement).addClass(directionalClassName)\n\n const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10)\n if (nextElementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = nextElementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n\n const transitionDuration = Util.getTransitionDurationFromElement(activeElement)\n\n $(activeElement)\n .one(Util.TRANSITION_END, () => {\n $(nextElement)\n .removeClass(`${directionalClassName} ${orderClassName}`)\n .addClass(ClassName.ACTIVE)\n\n $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)\n\n this._isSliding = false\n\n setTimeout(() => $(this._element).trigger(slidEvent), 0)\n })\n .emulateTransitionEnd(transitionDuration)\n } else {\n $(activeElement).removeClass(ClassName.ACTIVE)\n $(nextElement).addClass(ClassName.ACTIVE)\n\n this._isSliding = false\n $(this._element).trigger(slidEvent)\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n let _config = {\n ...Default,\n ...$(this).data()\n }\n\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (!data) {\n data = new Carousel(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n })\n }\n\n static _dataApiClickHandler(event) {\n const selector = Util.getSelectorFromElement(this)\n\n if (!selector) {\n return\n }\n\n const target = $(selector)[0]\n\n if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {\n return\n }\n\n const config = {\n ...$(target).data(),\n ...$(this).data()\n }\n const slideIndex = this.getAttribute('data-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel._jQueryInterface.call($(target), config)\n\n if (slideIndex) {\n $(target).data(DATA_KEY).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)\n\n$(window).on(Event.LOAD_DATA_API, () => {\n const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))\n for (let i = 0, len = carousels.length; i < len; i++) {\n const $carousel = $(carousels[i])\n Carousel._jQueryInterface.call($carousel, $carousel.data())\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Carousel._jQueryInterface\n$.fn[NAME].Constructor = Carousel\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Carousel._jQueryInterface\n}\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n toggle : true,\n parent : ''\n}\n\nconst DefaultType = {\n toggle : 'boolean',\n parent : '(string|element)'\n}\n\nconst Event = {\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SHOW : 'show',\n COLLAPSE : 'collapse',\n COLLAPSING : 'collapsing',\n COLLAPSED : 'collapsed'\n}\n\nconst Dimension = {\n WIDTH : 'width',\n HEIGHT : 'height'\n}\n\nconst Selector = {\n ACTIVES : '.show, .collapsing',\n DATA_TOGGLE : '[data-toggle=\"collapse\"]'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse {\n constructor(element, config) {\n this._isTransitioning = false\n this._element = element\n this._config = this._getConfig(config)\n this._triggerArray = [].slice.call(document.querySelectorAll(\n `[data-toggle=\"collapse\"][href=\"#${element.id}\"],` +\n `[data-toggle=\"collapse\"][data-target=\"#${element.id}\"]`\n ))\n\n const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = Util.getSelectorFromElement(elem)\n const filterElement = [].slice.call(document.querySelectorAll(selector))\n .filter((foundElem) => foundElem === element)\n\n if (selector !== null && filterElement.length > 0) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._parent = this._config.parent ? this._getParent() : null\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._element, this._triggerArray)\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle() {\n if ($(this._element).hasClass(ClassName.SHOW)) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning ||\n $(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n let actives\n let activesData\n\n if (this._parent) {\n actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))\n .filter((elem) => {\n if (typeof this._config.parent === 'string') {\n return elem.getAttribute('data-parent') === this._config.parent\n }\n\n return elem.classList.contains(ClassName.COLLAPSE)\n })\n\n if (actives.length === 0) {\n actives = null\n }\n }\n\n if (actives) {\n activesData = $(actives).not(this._selector).data(DATA_KEY)\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = $.Event(Event.SHOW)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n if (actives) {\n Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')\n if (!activesData) {\n $(actives).data(DATA_KEY, null)\n }\n }\n\n const dimension = this._getDimension()\n\n $(this._element)\n .removeClass(ClassName.COLLAPSE)\n .addClass(ClassName.COLLAPSING)\n\n this._element.style[dimension] = 0\n\n if (this._triggerArray.length) {\n $(this._triggerArray)\n .removeClass(ClassName.COLLAPSED)\n .attr('aria-expanded', true)\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .addClass(ClassName.SHOW)\n\n this._element.style[dimension] = ''\n\n this.setTransitioning(false)\n\n $(this._element).trigger(Event.SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning ||\n !$(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n const startEvent = $.Event(Event.HIDE)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n Util.reflow(this._element)\n\n $(this._element)\n .addClass(ClassName.COLLAPSING)\n .removeClass(ClassName.COLLAPSE)\n .removeClass(ClassName.SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n if (triggerArrayLength > 0) {\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const selector = Util.getSelectorFromElement(trigger)\n\n if (selector !== null) {\n const $elem = $([].slice.call(document.querySelectorAll(selector)))\n if (!$elem.hasClass(ClassName.SHOW)) {\n $(trigger).addClass(ClassName.COLLAPSED)\n .attr('aria-expanded', false)\n }\n }\n }\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n this.setTransitioning(false)\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .trigger(Event.HIDDEN)\n }\n\n this._element.style[dimension] = ''\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n }\n\n setTransitioning(isTransitioning) {\n this._isTransitioning = isTransitioning\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._parent = null\n this._element = null\n this._triggerArray = null\n this._isTransitioning = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n const hasWidth = $(this._element).hasClass(Dimension.WIDTH)\n return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT\n }\n\n _getParent() {\n let parent\n\n if (Util.isElement(this._config.parent)) {\n parent = this._config.parent\n\n // It's a jQuery object\n if (typeof this._config.parent.jquery !== 'undefined') {\n parent = this._config.parent[0]\n }\n } else {\n parent = document.querySelector(this._config.parent)\n }\n\n const selector =\n `[data-toggle=\"collapse\"][data-parent=\"${this._config.parent}\"]`\n\n const children = [].slice.call(parent.querySelectorAll(selector))\n $(children).each((i, element) => {\n this._addAriaAndCollapsedClass(\n Collapse._getTargetFromElement(element),\n [element]\n )\n })\n\n return parent\n }\n\n _addAriaAndCollapsedClass(element, triggerArray) {\n const isOpen = $(element).hasClass(ClassName.SHOW)\n\n if (triggerArray.length) {\n $(triggerArray)\n .toggleClass(ClassName.COLLAPSED, !isOpen)\n .attr('aria-expanded', isOpen)\n }\n }\n\n // Static\n\n static _getTargetFromElement(element) {\n const selector = Util.getSelectorFromElement(element)\n return selector ? document.querySelector(selector) : null\n }\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $this = $(this)\n let data = $this.data(DATA_KEY)\n const _config = {\n ...Default,\n ...$this.data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data && _config.toggle && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n if (!data) {\n data = new Collapse(this, _config)\n $this.data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.currentTarget.tagName === 'A') {\n event.preventDefault()\n }\n\n const $trigger = $(this)\n const selector = Util.getSelectorFromElement(this)\n const selectors = [].slice.call(document.querySelectorAll(selector))\n\n $(selectors).each(function () {\n const $target = $(this)\n const data = $target.data(DATA_KEY)\n const config = data ? 'toggle' : $trigger.data()\n Collapse._jQueryInterface.call($target, config)\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Collapse._jQueryInterface\n$.fn[NAME].Constructor = Collapse\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Collapse._jQueryInterface\n}\n\nexport default Collapse\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'dropdown'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\nconst SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key\nconst TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key\nconst ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key\nconst ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key\nconst RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)\nconst REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,\n KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DISABLED : 'disabled',\n SHOW : 'show',\n DROPUP : 'dropup',\n DROPRIGHT : 'dropright',\n DROPLEFT : 'dropleft',\n MENURIGHT : 'dropdown-menu-right',\n MENULEFT : 'dropdown-menu-left',\n POSITION_STATIC : 'position-static'\n}\n\nconst Selector = {\n DATA_TOGGLE : '[data-toggle=\"dropdown\"]',\n FORM_CHILD : '.dropdown form',\n MENU : '.dropdown-menu',\n NAVBAR_NAV : '.navbar-nav',\n VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n}\n\nconst AttachmentMap = {\n TOP : 'top-start',\n TOPEND : 'top-end',\n BOTTOM : 'bottom-start',\n BOTTOMEND : 'bottom-end',\n RIGHT : 'right-start',\n RIGHTEND : 'right-end',\n LEFT : 'left-start',\n LEFTEND : 'left-end'\n}\n\nconst Default = {\n offset : 0,\n flip : true,\n boundary : 'scrollParent',\n reference : 'toggle',\n display : 'dynamic'\n}\n\nconst DefaultType = {\n offset : '(number|string|function)',\n flip : 'boolean',\n boundary : '(string|element)',\n reference : '(string|element)',\n display : 'string'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown {\n constructor(element, config) {\n this._element = element\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n toggle() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this._element)\n const isActive = $(this._menu).hasClass(ClassName.SHOW)\n\n Dropdown._clearMenus()\n\n if (isActive) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n // Disable totally Popper.js for Dropdown in Navbar\n if (!this._inNavbar) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper.js (https://popper.js.org/)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = parent\n } else if (Util.isElement(this._config.reference)) {\n referenceElement = this._config.reference\n\n // Check if it's jQuery element\n if (typeof this._config.reference.jquery !== 'undefined') {\n referenceElement = this._config.reference[0]\n }\n }\n\n // If boundary is not `scrollParent`, then set position to `static`\n // to allow the menu to \"escape\" the scroll parent's boundaries\n // https://github.com/twbs/bootstrap/issues/24251\n if (this._config.boundary !== 'scrollParent') {\n $(parent).addClass(ClassName.POSITION_STATIC)\n }\n this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n $(parent).closest(Selector.NAVBAR_NAV).length === 0) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n show() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || $(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n hide() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || !$(this._menu).hasClass(ClassName.SHOW)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n const parent = Dropdown._getParentFromElement(this._element)\n\n $(parent).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._element).off(EVENT_KEY)\n this._element = null\n this._menu = null\n if (this._popper !== null) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Private\n\n _addEventListeners() {\n $(this._element).on(Event.CLICK, (event) => {\n event.preventDefault()\n event.stopPropagation()\n this.toggle()\n })\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...$(this._element).data(),\n ...config\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n return config\n }\n\n _getMenuElement() {\n if (!this._menu) {\n const parent = Dropdown._getParentFromElement(this._element)\n\n if (parent) {\n this._menu = parent.querySelector(Selector.MENU)\n }\n }\n return this._menu\n }\n\n _getPlacement() {\n const $parentDropdown = $(this._element.parentNode)\n let placement = AttachmentMap.BOTTOM\n\n // Handle dropup\n if ($parentDropdown.hasClass(ClassName.DROPUP)) {\n placement = AttachmentMap.TOP\n if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.TOPEND\n }\n } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {\n placement = AttachmentMap.RIGHT\n } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {\n placement = AttachmentMap.LEFT\n } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.BOTTOMEND\n }\n return placement\n }\n\n _detectNavbar() {\n return $(this._element).closest('.navbar').length > 0\n }\n\n _getOffset() {\n const offset = {}\n\n if (typeof this._config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this._config.offset(data.offsets, this._element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this._config.offset\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const popperConfig = {\n placement: this._getPlacement(),\n modifiers: {\n offset: this._getOffset(),\n flip: {\n enabled: this._config.flip\n },\n preventOverflow: {\n boundariesElement: this._config.boundary\n }\n }\n }\n\n // Disable Popper.js if we have a static display\n if (this._config.display === 'static') {\n popperConfig.modifiers.applyStyle = {\n enabled: false\n }\n }\n\n return popperConfig\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data) {\n data = new Dropdown(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n\n static _clearMenus(event) {\n if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||\n event.type === 'keyup' && event.which !== TAB_KEYCODE)) {\n return\n }\n\n const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const parent = Dropdown._getParentFromElement(toggles[i])\n const context = $(toggles[i]).data(DATA_KEY)\n const relatedTarget = {\n relatedTarget: toggles[i]\n }\n\n if (event && event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n if (!context) {\n continue\n }\n\n const dropdownMenu = context._menu\n if (!$(parent).hasClass(ClassName.SHOW)) {\n continue\n }\n\n if (event && (event.type === 'click' &&\n /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&\n $.contains(parent, event.target)) {\n continue\n }\n\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n $(parent).trigger(hideEvent)\n if (hideEvent.isDefaultPrevented()) {\n continue\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n toggles[i].setAttribute('aria-expanded', 'false')\n\n $(dropdownMenu).removeClass(ClassName.SHOW)\n $(parent)\n .removeClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n }\n\n static _getParentFromElement(element) {\n let parent\n const selector = Util.getSelectorFromElement(element)\n\n if (selector) {\n parent = document.querySelector(selector)\n }\n\n return parent || element.parentNode\n }\n\n // eslint-disable-next-line complexity\n static _dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName)\n ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&\n (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||\n $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this)\n const isActive = $(parent).hasClass(ClassName.SHOW)\n\n if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {\n if (event.which === ESCAPE_KEYCODE) {\n const toggle = parent.querySelector(Selector.DATA_TOGGLE)\n $(toggle).trigger('focus')\n }\n\n $(this).trigger('click')\n return\n }\n\n const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS))\n\n if (items.length === 0) {\n return\n }\n\n let index = items.indexOf(event.target)\n\n if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up\n index--\n }\n\n if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down\n index++\n }\n\n if (index < 0) {\n index = 0\n }\n\n items[index].focus()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document)\n .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)\n .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)\n .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n event.preventDefault()\n event.stopPropagation()\n Dropdown._jQueryInterface.call($(this), 'toggle')\n })\n .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {\n e.stopPropagation()\n })\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Dropdown._jQueryInterface\n$.fn[NAME].Constructor = Dropdown\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Dropdown._jQueryInterface\n}\n\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n\nconst Default = {\n backdrop : true,\n keyboard : true,\n focus : true,\n show : true\n}\n\nconst DefaultType = {\n backdrop : '(boolean|string)',\n keyboard : 'boolean',\n focus : 'boolean',\n show : 'boolean'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n RESIZE : `resize${EVENT_KEY}`,\n CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,\n KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,\n MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,\n MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n SCROLLABLE : 'modal-dialog-scrollable',\n SCROLLBAR_MEASURER : 'modal-scrollbar-measure',\n BACKDROP : 'modal-backdrop',\n OPEN : 'modal-open',\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n DIALOG : '.modal-dialog',\n MODAL_BODY : '.modal-body',\n DATA_TOGGLE : '[data-toggle=\"modal\"]',\n DATA_DISMISS : '[data-dismiss=\"modal\"]',\n FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',\n STICKY_CONTENT : '.sticky-top'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal {\n constructor(element, config) {\n this._config = this._getConfig(config)\n this._element = element\n this._dialog = element.querySelector(Selector.DIALOG)\n this._backdrop = null\n this._isShown = false\n this._isBodyOverflowing = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollbarWidth = 0\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n this._isTransitioning = true\n }\n\n const showEvent = $.Event(Event.SHOW, {\n relatedTarget\n })\n\n $(this._element).trigger(showEvent)\n\n if (this._isShown || showEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = true\n\n this._checkScrollbar()\n this._setScrollbar()\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(this._element).on(\n Event.CLICK_DISMISS,\n Selector.DATA_DISMISS,\n (event) => this.hide(event)\n )\n\n $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {\n $(this._element).one(Event.MOUSEUP_DISMISS, (event) => {\n if ($(event.target).is(this._element)) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide(event) {\n if (event) {\n event.preventDefault()\n }\n\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = $.Event(Event.HIDE)\n\n $(this._element).trigger(hideEvent)\n\n if (!this._isShown || hideEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = false\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (transition) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(document).off(Event.FOCUSIN)\n\n $(this._element).removeClass(ClassName.SHOW)\n\n $(this._element).off(Event.CLICK_DISMISS)\n $(this._dialog).off(Event.MOUSEDOWN_DISMISS)\n\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._element)\n\n $(this._element)\n .one(Util.TRANSITION_END, (event) => this._hideModal(event))\n .emulateTransitionEnd(transitionDuration)\n } else {\n this._hideModal()\n }\n }\n\n dispose() {\n [window, this._element, this._dialog]\n .forEach((htmlElement) => $(htmlElement).off(EVENT_KEY))\n\n /**\n * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`\n * Do not move `document` in `htmlElements` array\n * It will remove `Event.CLICK_DATA_API` event that should remain\n */\n $(document).off(Event.FOCUSIN)\n\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._element = null\n this._dialog = null\n this._backdrop = null\n this._isShown = null\n this._isBodyOverflowing = null\n this._ignoreBackdropClick = null\n this._isTransitioning = null\n this._scrollbarWidth = null\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const transition = $(this._element).hasClass(ClassName.FADE)\n\n if (!this._element.parentNode ||\n this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.appendChild(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n\n if ($(this._dialog).hasClass(ClassName.SCROLLABLE)) {\n this._dialog.querySelector(Selector.MODAL_BODY).scrollTop = 0\n } else {\n this._element.scrollTop = 0\n }\n\n if (transition) {\n Util.reflow(this._element)\n }\n\n $(this._element).addClass(ClassName.SHOW)\n\n if (this._config.focus) {\n this._enforceFocus()\n }\n\n const shownEvent = $.Event(Event.SHOWN, {\n relatedTarget\n })\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._element.focus()\n }\n this._isTransitioning = false\n $(this._element).trigger(shownEvent)\n }\n\n if (transition) {\n const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)\n\n $(this._dialog)\n .one(Util.TRANSITION_END, transitionComplete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n transitionComplete()\n }\n }\n\n _enforceFocus() {\n $(document)\n .off(Event.FOCUSIN) // Guard against infinite focus loop\n .on(Event.FOCUSIN, (event) => {\n if (document !== event.target &&\n this._element !== event.target &&\n $(this._element).has(event.target).length === 0) {\n this._element.focus()\n }\n })\n }\n\n _setEscapeEvent() {\n if (this._isShown && this._config.keyboard) {\n $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {\n if (event.which === ESCAPE_KEYCODE) {\n event.preventDefault()\n this.hide()\n }\n })\n } else if (!this._isShown) {\n $(this._element).off(Event.KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))\n } else {\n $(window).off(Event.RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._isTransitioning = false\n this._showBackdrop(() => {\n $(document.body).removeClass(ClassName.OPEN)\n this._resetAdjustments()\n this._resetScrollbar()\n $(this._element).trigger(Event.HIDDEN)\n })\n }\n\n _removeBackdrop() {\n if (this._backdrop) {\n $(this._backdrop).remove()\n this._backdrop = null\n }\n }\n\n _showBackdrop(callback) {\n const animate = $(this._element).hasClass(ClassName.FADE)\n ? ClassName.FADE : ''\n\n if (this._isShown && this._config.backdrop) {\n this._backdrop = document.createElement('div')\n this._backdrop.className = ClassName.BACKDROP\n\n if (animate) {\n this._backdrop.classList.add(animate)\n }\n\n $(this._backdrop).appendTo(document.body)\n\n $(this._element).on(Event.CLICK_DISMISS, (event) => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n if (event.target !== event.currentTarget) {\n return\n }\n if (this._config.backdrop === 'static') {\n this._element.focus()\n } else {\n this.hide()\n }\n })\n\n if (animate) {\n Util.reflow(this._backdrop)\n }\n\n $(this._backdrop).addClass(ClassName.SHOW)\n\n if (!callback) {\n return\n }\n\n if (!animate) {\n callback()\n return\n }\n\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callback)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else if (!this._isShown && this._backdrop) {\n $(this._backdrop).removeClass(ClassName.SHOW)\n\n const callbackRemove = () => {\n this._removeBackdrop()\n if (callback) {\n callback()\n }\n }\n\n if ($(this._element).hasClass(ClassName.FADE)) {\n const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callbackRemove)\n .emulateTransitionEnd(backdropTransitionDuration)\n } else {\n callbackRemove()\n }\n } else if (callback) {\n callback()\n }\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // todo (fat): these should probably be refactored out of modal.js\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing =\n this._element.scrollHeight > document.documentElement.clientHeight\n\n if (!this._isBodyOverflowing && isModalOverflowing) {\n this._element.style.paddingLeft = `${this._scrollbarWidth}px`\n }\n\n if (this._isBodyOverflowing && !isModalOverflowing) {\n this._element.style.paddingRight = `${this._scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n _checkScrollbar() {\n const rect = document.body.getBoundingClientRect()\n this._isBodyOverflowing = rect.left + rect.right < window.innerWidth\n this._scrollbarWidth = this._getScrollbarWidth()\n }\n\n _setScrollbar() {\n if (this._isBodyOverflowing) {\n // Note: DOMNode.style.paddingRight returns the actual value or '' if not set\n // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n const stickyContent = [].slice.call(document.querySelectorAll(Selector.STICKY_CONTENT))\n\n // Adjust fixed content padding\n $(fixedContent).each((index, element) => {\n const actualPadding = element.style.paddingRight\n const calculatedPadding = $(element).css('padding-right')\n $(element)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n })\n\n // Adjust sticky content margin\n $(stickyContent).each((index, element) => {\n const actualMargin = element.style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element)\n .data('margin-right', actualMargin)\n .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)\n })\n\n // Adjust body padding\n const actualPadding = document.body.style.paddingRight\n const calculatedPadding = $(document.body).css('padding-right')\n $(document.body)\n .data('padding-right', actualPadding)\n .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n }\n\n $(document.body).addClass(ClassName.OPEN)\n }\n\n _resetScrollbar() {\n // Restore fixed content padding\n const fixedContent = [].slice.call(document.querySelectorAll(Selector.FIXED_CONTENT))\n $(fixedContent).each((index, element) => {\n const padding = $(element).data('padding-right')\n $(element).removeData('padding-right')\n element.style.paddingRight = padding ? padding : ''\n })\n\n // Restore sticky content\n const elements = [].slice.call(document.querySelectorAll(`${Selector.STICKY_CONTENT}`))\n $(elements).each((index, element) => {\n const margin = $(element).data('margin-right')\n if (typeof margin !== 'undefined') {\n $(element).css('margin-right', margin).removeData('margin-right')\n }\n })\n\n // Restore body padding\n const padding = $(document.body).data('padding-right')\n $(document.body).removeData('padding-right')\n document.body.style.paddingRight = padding ? padding : ''\n }\n\n _getScrollbarWidth() { // thx d.walsh\n const scrollDiv = document.createElement('div')\n scrollDiv.className = ClassName.SCROLLBAR_MEASURER\n document.body.appendChild(scrollDiv)\n const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth\n document.body.removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n // Static\n\n static _jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = {\n ...Default,\n ...$(this).data(),\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (!data) {\n data = new Modal(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config](relatedTarget)\n } else if (_config.show) {\n data.show(relatedTarget)\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n let target\n const selector = Util.getSelectorFromElement(this)\n\n if (selector) {\n target = document.querySelector(selector)\n }\n\n const config = $(target).data(DATA_KEY)\n ? 'toggle' : {\n ...$(target).data(),\n ...$(this).data()\n }\n\n if (this.tagName === 'A' || this.tagName === 'AREA') {\n event.preventDefault()\n }\n\n const $target = $(target).one(Event.SHOW, (showEvent) => {\n if (showEvent.isDefaultPrevented()) {\n // Only register focus restorer if modal will actually get shown\n return\n }\n\n $target.one(Event.HIDDEN, () => {\n if ($(this).is(':visible')) {\n this.focus()\n }\n })\n })\n\n Modal._jQueryInterface.call($(target), config, this)\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Modal._jQueryInterface\n$.fn[NAME].Constructor = Modal\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Modal._jQueryInterface\n}\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tools/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttrs = [\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n]\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultWhitelist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i\n\nfunction allowedAttribute(attr, allowedAttributeList) {\n const attrName = attr.nodeName.toLowerCase()\n\n if (allowedAttributeList.indexOf(attrName) !== -1) {\n if (uriAttrs.indexOf(attrName) !== -1) {\n return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter((attrRegex) => attrRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, l = regExp.length; i < l; i++) {\n if (attrName.match(regExp[i])) {\n return true\n }\n }\n\n return false\n}\n\nexport function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {\n if (unsafeHtml.length === 0) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const whitelistKeys = Object.keys(whiteList)\n const elements = [].slice.call(createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const el = elements[i]\n const elName = el.nodeName.toLowerCase()\n\n if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {\n el.parentNode.removeChild(el)\n\n continue\n }\n\n const attributeList = [].slice.call(el.attributes)\n const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])\n\n attributeList.forEach((attr) => {\n if (!allowedAttribute(attr, whitelistedAttributes)) {\n el.removeAttribute(attr.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n DefaultWhitelist,\n sanitizeHtml\n} from './tools/sanitizer'\nimport $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-tooltip'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\nconst DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']\n\nconst DefaultType = {\n animation : 'boolean',\n template : 'string',\n title : '(string|element|function)',\n trigger : 'string',\n delay : '(number|object)',\n html : 'boolean',\n selector : '(string|boolean)',\n placement : '(string|function)',\n offset : '(number|string|function)',\n container : '(string|element|boolean)',\n fallbackPlacement : '(string|array)',\n boundary : '(string|element)',\n sanitize : 'boolean',\n sanitizeFn : '(null|function)',\n whiteList : 'object'\n}\n\nconst AttachmentMap = {\n AUTO : 'auto',\n TOP : 'top',\n RIGHT : 'right',\n BOTTOM : 'bottom',\n LEFT : 'left'\n}\n\nconst Default = {\n animation : true,\n template : '
' +\n '
' +\n '
',\n trigger : 'hover focus',\n title : '',\n delay : 0,\n html : false,\n selector : false,\n placement : 'top',\n offset : 0,\n container : false,\n fallbackPlacement : 'flip',\n boundary : 'scrollParent',\n sanitize : true,\n sanitizeFn : null,\n whiteList : DefaultWhitelist\n}\n\nconst HoverState = {\n SHOW : 'show',\n OUT : 'out'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TOOLTIP : '.tooltip',\n TOOLTIP_INNER : '.tooltip-inner',\n ARROW : '.arrow'\n}\n\nconst Trigger = {\n HOVER : 'hover',\n FOCUS : 'focus',\n CLICK : 'click',\n MANUAL : 'manual'\n}\n\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tooltip {\n constructor(element, config) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper.js (https://popper.js.org/)')\n }\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this.element = element\n this.config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const dataKey = this.constructor.DATA_KEY\n let context = $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n $.removeData(this.element, this.constructor.DATA_KEY)\n\n $(this.element).off(this.constructor.EVENT_KEY)\n $(this.element).closest('.modal').off('hide.bs.modal')\n\n if (this.tip) {\n $(this.tip).remove()\n }\n\n this._isEnabled = null\n this._timeout = null\n this._hoverState = null\n this._activeTrigger = null\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n this._popper = null\n this.element = null\n this.config = null\n this.tip = null\n }\n\n show() {\n if ($(this.element).css('display') === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n const showEvent = $.Event(this.constructor.Event.SHOW)\n if (this.isWithContent() && this._isEnabled) {\n $(this.element).trigger(showEvent)\n\n const shadowRoot = Util.findShadowRoot(this.element)\n const isInTheDom = $.contains(\n shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,\n this.element\n )\n\n if (showEvent.isDefaultPrevented() || !isInTheDom) {\n return\n }\n\n const tip = this.getTipElement()\n const tipId = Util.getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this.element.setAttribute('aria-describedby', tipId)\n\n this.setContent()\n\n if (this.config.animation) {\n $(tip).addClass(ClassName.FADE)\n }\n\n const placement = typeof this.config.placement === 'function'\n ? this.config.placement.call(this, tip, this.element)\n : this.config.placement\n\n const attachment = this._getAttachment(placement)\n this.addAttachmentClass(attachment)\n\n const container = this._getContainer()\n $(tip).data(this.constructor.DATA_KEY, this)\n\n if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {\n $(tip).appendTo(container)\n }\n\n $(this.element).trigger(this.constructor.Event.INSERTED)\n\n this._popper = new Popper(this.element, tip, {\n placement: attachment,\n modifiers: {\n offset: this._getOffset(),\n flip: {\n behavior: this.config.fallbackPlacement\n },\n arrow: {\n element: Selector.ARROW\n },\n preventOverflow: {\n boundariesElement: this.config.boundary\n }\n },\n onCreate: (data) => {\n if (data.originalPlacement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n },\n onUpdate: (data) => this._handlePopperPlacementChange(data)\n })\n\n $(tip).addClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().on('mouseover', null, $.noop)\n }\n\n const complete = () => {\n if (this.config.animation) {\n this._fixTransition()\n }\n const prevHoverState = this._hoverState\n this._hoverState = null\n\n $(this.element).trigger(this.constructor.Event.SHOWN)\n\n if (prevHoverState === HoverState.OUT) {\n this._leave(null, this)\n }\n }\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(this.tip)\n\n $(this.tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n }\n }\n\n hide(callback) {\n const tip = this.getTipElement()\n const hideEvent = $.Event(this.constructor.Event.HIDE)\n const complete = () => {\n if (this._hoverState !== HoverState.SHOW && tip.parentNode) {\n tip.parentNode.removeChild(tip)\n }\n\n this._cleanTipClass()\n this.element.removeAttribute('aria-describedby')\n $(this.element).trigger(this.constructor.Event.HIDDEN)\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n if (callback) {\n callback()\n }\n }\n\n $(this.element).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(tip).removeClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $(document.body).children().off('mouseover', null, $.noop)\n }\n\n this._activeTrigger[Trigger.CLICK] = false\n this._activeTrigger[Trigger.FOCUS] = false\n this._activeTrigger[Trigger.HOVER] = false\n\n if ($(this.tip).hasClass(ClassName.FADE)) {\n const transitionDuration = Util.getTransitionDurationFromElement(tip)\n\n $(tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(transitionDuration)\n } else {\n complete()\n }\n\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const tip = this.getTipElement()\n this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())\n $(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n setElementContent($element, content) {\n if (typeof content === 'object' && (content.nodeType || content.jquery)) {\n // Content is a DOM node or a jQuery\n if (this.config.html) {\n if (!$(content).parent().is($element)) {\n $element.empty().append(content)\n }\n } else {\n $element.text($(content).text())\n }\n\n return\n }\n\n if (this.config.html) {\n if (this.config.sanitize) {\n content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)\n }\n\n $element.html(content)\n } else {\n $element.text(content)\n }\n }\n\n getTitle() {\n let title = this.element.getAttribute('data-original-title')\n\n if (!title) {\n title = typeof this.config.title === 'function'\n ? this.config.title.call(this.element)\n : this.config.title\n }\n\n return title\n }\n\n // Private\n\n _getOffset() {\n const offset = {}\n\n if (typeof this.config.offset === 'function') {\n offset.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this.config.offset(data.offsets, this.element) || {}\n }\n\n return data\n }\n } else {\n offset.offset = this.config.offset\n }\n\n return offset\n }\n\n _getContainer() {\n if (this.config.container === false) {\n return document.body\n }\n\n if (Util.isElement(this.config.container)) {\n return $(this.config.container)\n }\n\n return $(document).find(this.config.container)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this.config.trigger.split(' ')\n\n triggers.forEach((trigger) => {\n if (trigger === 'click') {\n $(this.element).on(\n this.constructor.Event.CLICK,\n this.config.selector,\n (event) => this.toggle(event)\n )\n } else if (trigger !== Trigger.MANUAL) {\n const eventIn = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSEENTER\n : this.constructor.Event.FOCUSIN\n const eventOut = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSELEAVE\n : this.constructor.Event.FOCUSOUT\n\n $(this.element)\n .on(\n eventIn,\n this.config.selector,\n (event) => this._enter(event)\n )\n .on(\n eventOut,\n this.config.selector,\n (event) => this._leave(event)\n )\n }\n })\n\n $(this.element).closest('.modal').on(\n 'hide.bs.modal',\n () => {\n if (this.element) {\n this.hide()\n }\n }\n )\n\n if (this.config.selector) {\n this.config = {\n ...this.config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const titleType = typeof this.element.getAttribute('data-original-title')\n\n if (this.element.getAttribute('title') || titleType !== 'string') {\n this.element.setAttribute(\n 'data-original-title',\n this.element.getAttribute('title') || ''\n )\n\n this.element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER\n ] = true\n }\n\n if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {\n context._hoverState = HoverState.SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.SHOW\n\n if (!context.config.delay || !context.config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.SHOW) {\n context.show()\n }\n }, context.config.delay.show)\n }\n\n _leave(event, context) {\n const dataKey = this.constructor.DATA_KEY\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER\n ] = false\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.OUT\n\n if (!context.config.delay || !context.config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.OUT) {\n context.hide()\n }\n }, context.config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = $(this.element).data()\n\n Object.keys(dataAttributes)\n .forEach((dataAttr) => {\n if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n if (this.config) {\n for (const key in this.config) {\n if (this.constructor.Default[key] !== this.config[key]) {\n config[key] = this.config[key]\n }\n }\n }\n\n return config\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n _handlePopperPlacementChange(popperData) {\n const popperInstance = popperData.instance\n this.tip = popperInstance.popper\n this._cleanTipClass()\n this.addAttachmentClass(this._getAttachment(popperData.placement))\n }\n\n _fixTransition() {\n const tip = this.getTipElement()\n const initConfigAnimation = this.config.animation\n\n if (tip.getAttribute('x-placement') !== null) {\n return\n }\n\n $(tip).removeClass(ClassName.FADE)\n this.config.animation = false\n this.hide()\n this.show()\n this.config.animation = initConfigAnimation\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' && config\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Tooltip(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Tooltip._jQueryInterface\n$.fn[NAME].Constructor = Tooltip\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Tooltip._jQueryInterface\n}\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\nconst CLASS_PREFIX = 'bs-popover'\nconst BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\n\nconst Default = {\n ...Tooltip.Default,\n placement : 'right',\n trigger : 'click',\n content : '',\n template : '
' +\n '
' +\n '

' +\n '
'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content : '(string|element|function)'\n}\n\nconst ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n}\n\nconst Selector = {\n TITLE : '.popover-header',\n CONTENT : '.popover-body'\n}\n\nconst Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const $tip = $(this.getTipElement())\n\n // We use append for html objects to maintain js events\n this.setElementContent($tip.find(Selector.TITLE), this.getTitle())\n let content = this._getContent()\n if (typeof content === 'function') {\n content = content.call(this.element)\n }\n this.setElementContent($tip.find(Selector.CONTENT), content)\n\n $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n // Private\n\n _getContent() {\n return this.element.getAttribute('data-content') ||\n this.config.content\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length > 0) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Popover(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n$.fn[NAME] = Popover._jQueryInterface\n$.fn[NAME].Constructor = Popover\n$.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Popover._jQueryInterface\n}\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.3.1): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport $ from 'jquery'\nimport Util from './util'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst VERSION = '4.3.1'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst JQUERY_NO_CONFLICT = $.fn[NAME]\n\nconst Default = {\n offset : 10,\n method : 'auto',\n target : ''\n}\n\nconst DefaultType = {\n offset : 'number',\n method : 'string',\n target : '(string|element)'\n}\n\nconst Event = {\n ACTIVATE : `activate${EVENT_KEY}`,\n SCROLL : `scroll${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`\n}\n\nconst ClassName = {\n DROPDOWN_ITEM : 'dropdown-item',\n DROPDOWN_MENU : 'dropdown-menu',\n ACTIVE : 'active'\n}\n\nconst Selector = {\n DATA_SPY : '[data-spy=\"scroll\"]',\n ACTIVE : '.active',\n NAV_LIST_GROUP : '.nav, .list-group',\n NAV_LINKS : '.nav-link',\n NAV_ITEMS : '.nav-item',\n LIST_ITEMS : '.list-group-item',\n DROPDOWN : '.dropdown',\n DROPDOWN_ITEMS : '.dropdown-item',\n DROPDOWN_TOGGLE : '.dropdown-toggle'\n}\n\nconst OffsetMethod = {\n OFFSET : 'offset',\n POSITION : 'position'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy {\n constructor(element, config) {\n this._element = element\n this._scrollElement = element.tagName === 'BODY' ? window : element\n this._config = this._getConfig(config)\n this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +\n `${this._config.target} ${Selector.LIST_ITEMS},` +\n `${this._config.target} ${Selector.DROPDOWN_ITEMS}`\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window\n ? OffsetMethod.OFFSET : OffsetMethod.POSITION\n\n const offsetMethod = this._config.method === 'auto'\n ? autoMethod : this._config.method\n\n const offsetBase = offsetMethod === OffsetMethod.POSITION\n ? this._getScrollTop() : 0\n\n this._offsets = []\n this._targets = []\n\n this._scrollHeight = this._getScrollHeight()\n\n const targets = [].slice.call(document.querySelectorAll(this._selector))\n\n targets\n .map((element) => {\n let target\n const targetSelector = Util.getSelectorFromElement(element)\n\n if (targetSelector) {\n target = document.querySelector(targetSelector)\n }\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n // TODO (fat): remove sketch reliance on jQuery position/offset\n return [\n $(target)[offsetMethod]().top + offsetBase,\n targetSelector\n ]\n }\n }\n return null\n })\n .filter((item) => item)\n .sort((a, b) => a[0] - b[0])\n .forEach((item) => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._scrollElement).off(EVENT_KEY)\n\n this._element = null\n this._scrollElement = null\n this._config = null\n this._selector = null\n this._offsets = null\n this._targets = null\n this._activeTarget = null\n this._scrollHeight = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...typeof config === 'object' && config ? config : {}\n }\n\n if (typeof config.target !== 'string') {\n let id = $(config.target).attr('id')\n if (!id) {\n id = Util.getUID(NAME)\n $(config.target).attr('id', id)\n }\n config.target = `#${id}`\n }\n\n Util.typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window\n ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window\n ? window.innerHeight : this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset +\n scrollHeight -\n this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n const offsetLength = this._offsets.length\n for (let i = offsetLength; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' ||\n scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = this._selector\n .split(',')\n .map((selector) => `${selector}[data-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))\n\n if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {\n $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)\n $link.addClass(ClassName.ACTIVE)\n } else {\n // Set triggered link as active\n $link.addClass(ClassName.ACTIVE)\n // Set triggered links parents as active\n // With both