diff --git a/.gitignore b/.gitignore index 6e144da..ef2435c 100755 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,8 @@ composer.phar /caches/*/ caches/last_settings_migrate_run .php-cs-fixer.cache +/storage/caches/*.json +/storage/caches/*.txt +/storage/caches/*.log +/storage/caches/*/ +/storage/logs/*.log diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e6f7d3..249c888 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,34 @@ { "php.version": "8.3", "cSpell.words": [ - "tailwindcss" - ] + "acaches", + "asuccessful", + "asuccessfully", + "autoloader", + "ccaches", + "dbconfigure", + "ealready", + "ERRMODE", + "fklmnor", + "Inno", + "jsyaml", + "Kutzu", + "mythicalcore", + "mythicalframework", + "rfiles", + "Swal", + "tailwindcss", + "unban", + "unic", + "unverification", + "unverify" + ], + "cSpell.useGitignore": true, + "cSpell.ignorePaths": [ + "settings.json", + "composer-lock.json", + ], + "window.title": "${appName}", + "window.autoDetectColorScheme": true, + "editor.minimap.enabled": true, } \ No newline at end of file diff --git a/.vscode/vscode.code-snippets b/.vscode/vscode.code-snippets index 6b0370a..e5fa048 100755 --- a/.vscode/vscode.code-snippets +++ b/.vscode/vscode.code-snippets @@ -97,10 +97,26 @@ }, "Get translation": { "prefix": "S_GetLang", - "scope": "php", + "scope": "twig", "body": [ - "\\$lang['$1']" + "{{ lang('$1') }}" ], "description": "Get a specific value from the language file!" }, + "Get setting": { + "prefix": "S_GetSetting", + "scope": "twig", + "body": [ + "{{ setting('$1', '$2') }}" + ], + "description": "Get a specific value from the settings table!" + }, + "Get Config": { + "prefix": "S_GetConfig", + "scope": "twig", + "body": [ + "{{ cfg('$1', '$2') }}" + ], + "description": "Get a specific value from the config file!" + }, } \ No newline at end of file diff --git a/api/System/getTranslationKey.php b/api/System/getTranslationKey.php new file mode 100755 index 0000000..a7ed18d --- /dev/null +++ b/api/System/getTranslationKey.php @@ -0,0 +1,38 @@ + $translation, 'result' => $translation, 'text' => $translation, 'message' => $translation, 'error' => $error]); + } + } else { + api::BadRequest('You are missing the GET field for orElse!', []); + } +} else { + api::BadRequest('You are missing the GET field for key!', []); +} diff --git a/api/User/infoalreadyexists.php b/api/User/infoalreadyexists.php index c43a8b5..12df5da 100755 --- a/api/User/infoalreadyexists.php +++ b/api/User/infoalreadyexists.php @@ -2,22 +2,57 @@ use MythicalSystems\Api\Api as api; use MythicalSystemsFramework\User\UserHelper; +use MythicalSystemsFramework\Kernel\Encryption; api::init(); api::allowOnlyGET(); if (isset($_GET['info']) && !$_GET['info'] == '') { if (isset($_GET['value']) && !$_GET['value'] == '') { - $info = $_GET['info']; - $value = $_GET['value']; - $user = UserHelper::doesInfoAboutExist($info, $value); - if ($user == 'INFO_EXISTS') { - api::OK('The info exists!', ['RESULT' => $user]); - } elseif ($user == 'INFO_NOT_FOUND') { - api::BadRequest('The info does not exist!', ['RESULT' => $user]); + if (isset($_GET['isEncrypted']) && !$_GET['isEncrypted'] == '') { + $isEncrypted = $_GET['isEncrypted']; + $info = $_GET['info']; + $value = $_GET['value']; + + if (isset($_GET['inVerted']) && !$_GET['inVerted'] == '') { + if ($_GET['inVerted'] == 'true') { + $inVerted = true; + } else { + $inVerted = false; + } + } else { + $inVerted = false; + } + + if ($isEncrypted == 'true') { + $value = Encryption::encrypt($value); + $user = UserHelper::doesInfoAboutExist($info, $value); + } else { + $user = UserHelper::doesInfoAboutExist($info, $value); + } + + if ($user == 'INFO_EXISTS') { + if ($inVerted == true) { + api::BadRequest('The info exists!', ['RESULT' => $user]); + } else { + api::OK('The info exists!', ['RESULT' => $user]); + } + } elseif ($user == 'INFO_NOT_FOUND') { + if ($inVerted == true) { + api::OK('The info does not exist!', ['RESULT' => $user]); + } else { + api::BadRequest('The info does not exist!', ['RESULT' => $user]); + } + } elseif ($user == 'ERROR_DATABASE_SELECT_FAILED') { + api::BadRequest('Failed to select the info from the database!', ['RESULT' => $user]); + } else { + api::BadRequest('An unknown error occurred!', ['RESULT' => $user]); + } + } else { + api::BadRequest('You are missing the GET field for isEncrypted!', []); } } else { - api::BadRequest('You are missing the post field for value!', []); + api::BadRequest('You are missing the GET field for value!', []); } } else { - api::BadRequest('You are missing the post field for info!', []); + api::BadRequest('You are missing the GET field for info!', []); } diff --git a/api/User/register.php b/api/User/register.php index 7c0017b..8e31de2 100755 --- a/api/User/register.php +++ b/api/User/register.php @@ -1,8 +1,11 @@ $user]); } else { - $user_id = user::getSpecificUserData($user, 'uuid', false); + $user_id = user::getSpecificUserData($user, 'uuid', true); if (MailService::isEnabled() == true) { // TODO: Add a verify system } else { user::updateSpecificUserData($user, 'verified', 'true'); - ActivityHandler::addActivity($user_id, user::getSpecificUserData($user, 'username', false), 'User created an account!', MythicalSystems\CloudFlare\CloudFlare::getRealUserIP(), 'USER_CREATED'); - ActivityHandler::addActivity($user_id, user::getSpecificUserData($user, 'username', false), 'User verified his account!', MythicalSystems\CloudFlare\CloudFlare::getRealUserIP(), 'USER_VERIFIED'); + + ActivityHandler::addActivity($user_id, user::getSpecificUserData($user, 'username', true), 'User created an account!', MythicalSystems\CloudFlare\CloudFlare::getRealUserIP(), 'USER_CREATED'); + ActivityHandler::addActivity($user_id, user::getSpecificUserData($user, 'username', true), 'User verified his account!', MythicalSystems\CloudFlare\CloudFlare::getRealUserIP(), 'USER_VERIFIED'); } api::OK('The user has been created!', ['TOKEN' => $user]); } } catch (Exception $e) { + api::InternalServerError('An error occurred!', ['ERROR' => $e->getMessage()]); + Logger::log(LoggerTypes::CORE, LoggerLevels::CRITICAL, '(Api/User/register.php) An error occurred in the register.php file!'); } diff --git a/app/App.php b/app/App.php index 549f385..9cebd25 100755 --- a/app/App.php +++ b/app/App.php @@ -5,14 +5,13 @@ class App extends \MythicalSystems\Main { /** - * Convert a string to a bool. + * Call the garbage collector. */ - public static function convertStringToBool(string $value): bool + public static function callGarbageCollector(): void { - if ($value == 'true') { - return true; - } else { - return false; - } + gc_enable(); + gc_mem_caches(); + gc_collect_cycles(); + gc_disable(); } } diff --git a/app/Cli/CommandBuilder.php b/app/Cli/CommandBuilder.php new file mode 100755 index 0000000..86565ed --- /dev/null +++ b/app/Cli/CommandBuilder.php @@ -0,0 +1,16 @@ + 0) { usort($sqlFiles, function ($a, $b) { @@ -282,7 +280,7 @@ public static function migrate(bool $isCli = false): void echo Kernel::translateColorsCode('&cNo migrations found!&o'); echo Kernel::NewLine(); } else { - throw new NoMigrationsFound(); + throw new \Exception('No migrations found!'); } } } catch (\PDOException $e) { @@ -290,7 +288,7 @@ public static function migrate(bool $isCli = false): void echo Kernel::translateColorsCode('&cFailed to migrate the database: ' . $e->getMessage() . '&o'); echo Kernel::NewLine(); } else { - throw new MySQLError('Failed to migrate the database: ' . $e->getMessage()); + throw new \Exception('Failed to migrate the database: ' . $e->getMessage()); } } } diff --git a/app/Database/MySQLCache.php b/app/Database/MySQLCache.php index fe5e728..552f385 100755 --- a/app/Database/MySQLCache.php +++ b/app/Database/MySQLCache.php @@ -2,6 +2,7 @@ namespace MythicalSystemsFramework\Database; +use MythicalSystemsFramework\Kernel\Debugger; use MythicalSystemsFramework\Managers\Settings; class MySQLCache extends MySQL @@ -14,6 +15,7 @@ class MySQLCache extends MySQL public static function saveCache(string $table_name): string { try { + Debugger::HideAllErrors(); if (self::doesTableExist($table_name) == false) { return 'ERROR_TABLE_DOES_NOT_EXIST'; } diff --git a/app/Database/Redis/Redis.php b/app/Database/Redis/Redis.php deleted file mode 100755 index 57545f8..0000000 --- a/app/Database/Redis/Redis.php +++ /dev/null @@ -1,10 +0,0 @@ -connectMYSQLI(); + try { + $mysql = new MySQL(); + $conn = $mysql->connectMYSQLI(); - $stmt = $conn->prepare('SELECT * FROM framework_firewall WHERE ip = ?'); - $stmt->bind_param('s', $ip); - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + $stmt = $conn->prepare('SELECT * FROM framework_firewall WHERE ip = ?'); + $stmt->bind_param('s', $ip); + $stmt->execute(); + $result = $stmt->get_result(); + $stmt->close(); - if ($result->num_rows > 0) { - $row = $result->fetch_assoc(); - $action = $row['action']; + if ($result->num_rows > 0) { + $row = $result->fetch_assoc(); + $action = $row['action']; - if ($action === 'DROP' || $action === 'NONE' || $action === 'ALLOW') { - return $action; + if ($action === 'DROP' || $action === 'NONE' || $action === 'ALLOW') { + return $action; + } else { + return 'DATABASE_ERROR'; + } } else { - return 'DATABASE_ERROR'; + self::addIP($ip); + + return 'NONE'; } - } else { - self::addIP($ip); + } catch (\Exception $e) { + Logger::log(LoggerTypes::DATABASE, LoggerLevels::ERROR, '(App/Firewall/Firewall.php) Failed to check ip in database: ' . $e->__toString()); - return 'NONE'; + return 'DATABASE_ERROR'; } } @@ -58,7 +64,7 @@ public static function addIP(string $ip): string $conn = $mysql->connectMYSQLI(); $stmt = $conn->prepare('INSERT INTO framework_firewall (ip, action) VALUES (?, ?)'); - $type = Types::NONE; + $type = Events::NONE; $stmt->bind_param('ss', $ip, $type); $stmt->execute(); $stmt->close(); @@ -73,6 +79,11 @@ public static function addIP(string $ip): string /** * Assign a owner for a ip address! + * + * @param string $ip The ip of the user <3 + * @param string $uuid The uuid of the user <3 + * + * @return string The status of the operation. (OWNER_ASSIGNED/DATABASE_ERROR) */ public static function assignOwnership(string $ip, string $uuid): string { @@ -82,7 +93,7 @@ public static function assignOwnership(string $ip, string $uuid): string $mysql = new MySQL(); $conn = $mysql->connectMYSQLI(); - $stmt = $conn->prepare('UPDATE framework_firewall SET owner = ? WHERE ip = ?'); + $stmt = $conn->prepare('UPDATE framework_firewall SET uuid = ? WHERE ip = ?'); $stmt->bind_param('ss', $uuid, $ip); $stmt->execute(); $stmt->close(); @@ -94,4 +105,58 @@ public static function assignOwnership(string $ip, string $uuid): string return 'DATABASE_ERROR'; } } + + /** + * Remove the ownership of a ip address! + * + * @param string $ip The ip of the user <3 + * + * @return string (OWNER_REMOVED/DATABASE_ERROR) + */ + public static function removeOwnership(string $ip): string + { + try { + $mysql = new MySQL(); + $conn = $mysql->connectMYSQLI(); + + $stmt = $conn->prepare('UPDATE framework_firewall SET uuid = NULL WHERE ip = ?'); + $stmt->bind_param('s', $ip); + $stmt->execute(); + $stmt->close(); + + return 'OWNER_REMOVED'; + } catch (\Exception $e) { + Logger::log(LoggerTypes::DATABASE, LoggerLevels::ERROR, '(App/Firewall/Firewall.php) Failed to remove ownership from ip: ' . $e->__toString()); + + return 'DATABASE_ERROR'; + } + } + + /** + * Update the action of a ip address! + * + * @param string $ip The ip of the user <3 + * @param Events|string $action The action you want to set! + * + * @return string (ACTION_UPDATED/DATABASE_ERROR) + */ + public static function updateAction(string $ip, Events|string $action): string + { + try { + $mysql = new MySQL(); + $conn = $mysql->connectMYSQLI(); + + $stmt = $conn->prepare('UPDATE framework_firewall SET action = ? WHERE ip = ?'); + + $stmt->bind_param('ss', $action, $ip); + $stmt->execute(); + $stmt->close(); + + return 'ACTION_UPDATED'; + } catch (\Exception $e) { + Logger::log(LoggerTypes::DATABASE, LoggerLevels::ERROR, '(App/Firewall/Firewall.php) Failed to update action of ip: ' . $e->__toString()); + + return 'DATABASE_ERROR'; + } + } } diff --git a/app/Handlers/CacheHandler.php b/app/Handlers/CacheHandler.php index 00fa469..fb01d94 100755 --- a/app/Handlers/CacheHandler.php +++ b/app/Handlers/CacheHandler.php @@ -6,7 +6,7 @@ class CacheHandler { - public static $cache_file = __DIR__ . '/../../caches/cache.json'; + public static $cache_file = __DIR__ . '/../../storage/caches/cache.json'; /** * Create the cache file. diff --git a/app/Managers/ConfigManager.php b/app/Managers/ConfigManager.php index 3500cc7..9333624 100755 --- a/app/Managers/ConfigManager.php +++ b/app/Managers/ConfigManager.php @@ -6,7 +6,7 @@ class ConfigManager { - private static string $configpath = __DIR__ . '/../../settings.json'; + private static string $configpath = __DIR__ . '/../../storage/settings.json'; /** * DEPRECATED: Use Settings class instead!! diff --git a/app/Managers/LanguageManager.php b/app/Managers/LanguageManager.php index 7cd537b..498b1b9 100755 --- a/app/Managers/LanguageManager.php +++ b/app/Managers/LanguageManager.php @@ -14,11 +14,11 @@ class LanguageManager public static function getLang(): mixed { try { - $fallback_lang = __DIR__ . '/../../lang/en_US.php'; + $fallback_lang = __DIR__ . '/../../storage/lang/en_US.php'; if (file_exists($fallback_lang)) { $langConfig = Settings::getSetting('app', 'lang'); if (!$langConfig == '') { - $langFilePath = __DIR__ . '/../../lang/' . $langConfig . '.php'; + $langFilePath = __DIR__ . '/../../storage/lang/' . $langConfig . '.php'; if (file_exists($langFilePath)) { return include $langFilePath; } else { @@ -47,7 +47,7 @@ public static function getLang(): mixed */ public static function getAllAvailableLanguages(): array { - $langFiles = scandir(__DIR__ . '/../../lang/'); + $langFiles = scandir(__DIR__ . '/../../storage/lang/'); $langFiles = array_diff($langFiles, ['..', '.']); return $langFiles; diff --git a/app/Managers/Settings.php b/app/Managers/Settings.php index 1baf265..f302310 100755 --- a/app/Managers/Settings.php +++ b/app/Managers/Settings.php @@ -9,7 +9,7 @@ class Settings { - public static string $cache_path = __DIR__ . '/../../caches'; + public static string $cache_path = __DIR__ . '/../../storage/caches'; public static function up(): void { @@ -77,7 +77,7 @@ public static function migrate(bool $isTerminal = false): void $mysql = new MySQL(); $db = $mysql->connectPDO(); $db->exec('CREATE TABLE IF NOT EXISTS `framework_settings_migrations` (`id` INT NOT NULL AUTO_INCREMENT , `script` TEXT NOT NULL , `executed_at` DATETIME NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB; ALTER TABLE `framework_settings_migrations` CHANGE `executed_at` `executed_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;'); - $phpFiles = glob(__DIR__ . '/../../migrate/config/*.php'); + $phpFiles = glob(__DIR__ . '/../../storage/migrate/config/*.php'); if (count($phpFiles) > 0) { sort($phpFiles); diff --git a/app/Plugins/PluginsManager.php b/app/Plugins/PluginsManager.php index ad1575c..f81f8be 100755 --- a/app/Plugins/PluginsManager.php +++ b/app/Plugins/PluginsManager.php @@ -10,7 +10,7 @@ class PluginsManager public static function getAllPlugins(): array { $plugins = []; - $addonsDir = __DIR__ . '/../../addons'; + $addonsDir = __DIR__ . '/../../storage/addons'; $addonDirs = scandir($addonsDir); foreach ($addonDirs as $addonDir) { diff --git a/app/User/UserDataHandler.php b/app/User/UserDataHandler.php index bc0f864..be21b6e 100755 --- a/app/User/UserDataHandler.php +++ b/app/User/UserDataHandler.php @@ -11,6 +11,7 @@ namespace MythicalSystemsFramework\User; use Gravatar\Gravatar; +use MythicalSystemsFramework\Database\MySQL; use MythicalSystemsFramework\Kernel\LoggerTypes; use MythicalSystemsFramework\Kernel\LoggerLevels; use MythicalSystemsFramework\Kernel\Logger as logger; @@ -32,7 +33,7 @@ public static function login(string $email, string $password, string $ip): ?stri { try { // Connect to the database - $database = new \MythicalSystemsFramework\Database\MySQL(); + $database = new MySQL(); $mysqli = $database->connectMYSQLI(); // Check if the user exists $stmt = $mysqli->prepare('SELECT COUNT(*) FROM framework_users WHERE email = ? OR username = ?'); @@ -102,7 +103,7 @@ public static function create(string $username, string $password, string $email, $email = enc::encrypt($email); // Connect to the database - $database = new \MythicalSystemsFramework\Database\MySQL(); + $database = new MySQL(); $mysqli = $database->connectMYSQLI(); // New gravatar instance for avatars! $gravatar = new Gravatar([], true); @@ -171,7 +172,7 @@ public static function getSpecificUserData(string $account_token, string $data, return 'ERROR_ACCOUNT_NOT_VALID'; } // Connect to the database - $database = new \MythicalSystemsFramework\Database\MySQL(); + $database = new MySQL(); $mysqli = $database->connectMYSQLI(); // Check if the user exists $stmt = $mysqli->prepare('SELECT * FROM framework_users WHERE token = ?'); @@ -204,7 +205,7 @@ public static function getSpecificUserData(string $account_token, string $data, * @param string $value The value you want to set! * @param bool $encrypted Set to false in case the data is not encrypted! * - * @return bool True if the data was updated false if not! + * @return string (ERROR_ACCOUNT_NOT_VALID,ERROR_RECORD_IS_LOCKED,ERROR_DATABASE_UPDATE_FAILED,SUCCESS) */ public static function updateSpecificUserData(string $account_token, string $data, string $value, bool $encrypted = true): string { @@ -213,9 +214,18 @@ public static function updateSpecificUserData(string $account_token, string $dat if (!$isAccountValid) { return 'ERROR_ACCOUNT_NOT_VALID'; } + // Connect to the database - $database = new \MythicalSystemsFramework\Database\MySQL(); + $database = new MySQL(); $mysqli = $database->connectMYSQLI(); + + if (MySQL::getLock('framework_users', self::getSpecificUserData($account_token, 'id', false)) == true) { + logger::log(LoggerLevels::WARNING, LoggerTypes::DATABASE, '(App/User/UserDataHandler.php) Illegally tried to update a locked record!'); + + return 'ERROR_RECORD_IS_LOCKED'; + } + + MySQL::requestLock('framework_users', self::getSpecificUserData($account_token, 'id', false)); // Check if the user exists $stmt = $mysqli->prepare("UPDATE framework_users SET $data = ? WHERE token = ?"); if ($encrypted) { @@ -224,6 +234,7 @@ public static function updateSpecificUserData(string $account_token, string $dat $stmt->bind_param('ss', $value, $account_token); $stmt->execute(); $stmt->close(); + MySQL::requestUnLock('framework_users', self::getSpecificUserData($account_token, 'id', false)); return 'SUCCESS'; } catch (\Exception $e) { diff --git a/app/User/UserHelper.php b/app/User/UserHelper.php index c6c0315..d216bfb 100755 --- a/app/User/UserHelper.php +++ b/app/User/UserHelper.php @@ -32,7 +32,7 @@ public static function banUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_BANNED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -59,7 +59,7 @@ public static function unbanUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_UNBANNED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -160,7 +160,7 @@ public static function deleteUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_DELETED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -187,7 +187,7 @@ public static function restoreUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_RESTORED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -214,7 +214,7 @@ public static function isUserDeleted(string $account_token): string if ($delete_state == 'false') { return 'USER_NOT_DELETED'; } else { - return 'USER_DELETED'; + return $delete_state; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -241,7 +241,7 @@ public static function isUserVerified(string $account_token): string if ($verified_state == 'false') { return 'USER_NOT_VERIFIED'; } else { - return 'USER_VERIFIED'; + return $verified_state; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -268,7 +268,7 @@ public static function verifyUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_VERIFIED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -295,7 +295,7 @@ public static function unverifyUser(string $account_token): string if ($update_user == 'SUCCESS') { return 'USER_UNVERIFIED'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -323,10 +323,10 @@ public static function updateLastSeen(string $account_token, string $ip): string if ($update_user == 'SUCCESS') { return 'SUCCESS'; } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { - return 'ERROR_DATABASE_UPDATE_FAILED'; + return $update_user; } } else { return 'ERROR_ACCOUNT_NOT_VALID'; @@ -393,26 +393,24 @@ public static function doesUserHavePermission(string $account_token, string $per /** * Does the info exist? */ - public static function doesInfoAboutExist(string $info, string $username): string + public static function doesInfoAboutExist(string $info, string $value): string { try { $mysql = new MySQL(); $conn = $mysql->connectMYSQLI(); - $stmt = $conn->prepare('SELECT COUNT(*) FROM framework_users WHERE ? = ?'); + $stmt = $conn->prepare('SELECT COUNT(*) FROM framework_users WHERE ' . mysqli_real_escape_string($conn, $info) . ' = ?'); if (!$stmt) { Logger::log(LoggerLevels::ERROR, LoggerTypes::DATABASE, '(App/User/UserHelper.php) Failed to prepare statement: ' . $conn->error); return 'ERROR_DATABASE_SELECT_FAILED'; } - $stmt->bind_param('ss', $info, $username); + $stmt->bind_param('s', $value); $stmt->execute(); $stmt->bind_result($count); $stmt->fetch(); - $stmt->close(); - $conn->close(); if ($count > 0) { return 'INFO_EXISTS'; diff --git a/composer.json b/composer.json index 950658f..3dd1b71 100755 --- a/composer.json +++ b/composer.json @@ -62,16 +62,13 @@ ], "push": [ "export COMPOSER_ALLOW_SUPERUSER=1", - "bash devtools/push.bash", "git push origin develop", "composer run tests", - "composer run lint", - "bash devtools/count_php_lines.bash" + "composer run lint" ], "pull": [ "export COMPOSER_ALLOW_SUPERUSER=1", - "git pull origin develop", - "bash devtools/restore.bash" + "git pull origin develop" ], "install:dependency": [ "export COMPOSER_ALLOW_SUPERUSER=1", diff --git a/composer.lock b/composer.lock index 520761f..3de11a6 100755 --- a/composer.lock +++ b/composer.lock @@ -66,21 +66,22 @@ }, { "name": "mythicalsystems/core", - "version": "1.0.0.8", + "version": "1.0.0.10", "source": { "type": "git", "url": "https://github.com/MythicalLTD/MythicalCore.git", - "reference": "bb0a392af9fa9f5af90a69293672b3139af41246" + "reference": "5e1478f0ee9dc19a5bf7148c2435d09bc4daf927" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MythicalLTD/MythicalCore/zipball/bb0a392af9fa9f5af90a69293672b3139af41246", - "reference": "bb0a392af9fa9f5af90a69293672b3139af41246", + "url": "https://api.github.com/repos/MythicalLTD/MythicalCore/zipball/5e1478f0ee9dc19a5bf7148c2435d09bc4daf927", + "reference": "5e1478f0ee9dc19a5bf7148c2435d09bc4daf927", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", + "ext-sodium": "*", "php": ">=8.1.0" }, "replace": { @@ -131,7 +132,7 @@ "docs": "https://docs.mythicalsystems.me", "forum": "https://discord.gg/Tswkrhreu3", "issues": "https://github.com/mythicalltd/mythicalcore/issues", - "source": "https://github.com/MythicalLTD/MythicalCore/tree/1.0.0.8", + "source": "https://github.com/MythicalLTD/MythicalCore/tree/1.0.0.10", "wiki": "https://docs.mythicalsystems.me" }, "funding": [ @@ -140,7 +141,7 @@ "type": "GitHub" } ], - "time": "2024-07-09T09:43:28+00:00" + "time": "2024-08-19T18:32:19+00:00" }, { "name": "symfony/deprecation-contracts", @@ -672,26 +673,26 @@ }, { "name": "composer/pcre", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90" + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90", - "reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90", + "url": "https://api.github.com/repos/composer/pcre/zipball/1637e067347a0c40bbb1e3cd786b20dcab556a81", + "reference": "1637e067347a0c40bbb1e3cd786b20dcab556a81", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.8" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8 || ^9" }, @@ -731,7 +732,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.2.0" + "source": "https://github.com/composer/pcre/tree/3.3.0" }, "funding": [ { @@ -747,7 +748,7 @@ "type": "tidelift" } ], - "time": "2024-07-25T09:36:02+00:00" + "time": "2024-08-19T19:43:53+00:00" }, { "name": "composer/semver", @@ -1668,16 +1669,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.3.0", + "version": "11.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a8dce73a8938dfec7ac0daa91bdbcaae7d7188a3" + "reference": "fe179875ef0c14e90b75617002767eae0a742641" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a8dce73a8938dfec7ac0daa91bdbcaae7d7188a3", - "reference": "a8dce73a8938dfec7ac0daa91bdbcaae7d7188a3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fe179875ef0c14e90b75617002767eae0a742641", + "reference": "fe179875ef0c14e90b75617002767eae0a742641", "shasum": "" }, "require": { @@ -1698,7 +1699,7 @@ "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.0.1", + "sebastian/comparator": "^6.0.2", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", @@ -1748,7 +1749,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.3.0" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.3.1" }, "funding": [ { @@ -1764,7 +1765,7 @@ "type": "tidelift" } ], - "time": "2024-08-02T03:56:43+00:00" + "time": "2024-08-13T06:14:23+00:00" }, { "name": "psr/container", @@ -2621,16 +2622,16 @@ }, { "name": "sebastian/comparator", - "version": "6.0.1", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "131942b86d3587291067a94f295498ab6ac79c20" + "reference": "450d8f237bd611c45b5acf0733ce43e6bb280f81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/131942b86d3587291067a94f295498ab6ac79c20", - "reference": "131942b86d3587291067a94f295498ab6ac79c20", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/450d8f237bd611c45b5acf0733ce43e6bb280f81", + "reference": "450d8f237bd611c45b5acf0733ce43e6bb280f81", "shasum": "" }, "require": { @@ -2686,7 +2687,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.2" }, "funding": [ { @@ -2694,7 +2695,7 @@ "type": "github" } ], - "time": "2024-07-03T04:48:07+00:00" + "time": "2024-08-12T06:07:25+00:00" }, { "name": "sebastian/complexity", diff --git a/cron/bash/fix_permissions.bash b/cron/bash/fix_permissions.bash new file mode 100755 index 0000000..cf9023d --- /dev/null +++ b/cron/bash/fix_permissions.bash @@ -0,0 +1,2 @@ +chown -R www-data:www-data /var/www/framework/ +chmod -R 777 /var/www/framework/ \ No newline at end of file diff --git a/cron/jobs/SettingsJob.php b/cron/php/SettingsJob.php similarity index 100% rename from cron/jobs/SettingsJob.php rename to cron/php/SettingsJob.php diff --git a/cron/jobs/cacheJob.php b/cron/php/cacheJob.php similarity index 100% rename from cron/jobs/cacheJob.php rename to cron/php/cacheJob.php diff --git a/cron/runner.bash b/cron/runner.bash new file mode 100755 index 0000000..86beade --- /dev/null +++ b/cron/runner.bash @@ -0,0 +1,13 @@ +#!/bin/bash + +# Path to the bash directory +bash_dir="./bash" + +# Iterate over all .bash files in the directory +for file in "$bash_dir"/*.bash; do + # Check if the file is a regular file + if [[ -f "$file" ]]; then + # Execute the bash file + bash "$file" + fi +done \ No newline at end of file diff --git a/cron/runner.php b/cron/runner.php index 24eb853..241cecc 100755 --- a/cron/runner.php +++ b/cron/runner.php @@ -28,7 +28,7 @@ date_default_timezone_set('UTC'); } -$jobsDirectory = __DIR__ . '/jobs'; +$jobsDirectory = __DIR__ . '/php'; $files = scandir($jobsDirectory); foreach ($files as $file) { if ($file !== '.' && $file !== '..') { diff --git a/devtools/count_php_lines.bash b/devtools/count_php_lines.bash deleted file mode 100755 index 0d26394..0000000 --- a/devtools/count_php_lines.bash +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Set the directory to search for PHP files -directory="./" - -# Initialize the line count -line_count=0 - -apt install bc -y >> /dev/null 2>&1 - -# Function to recursively count lines of code in PHP files -count_lines() { - local dir="$1" - local files=$(find "$dir" -type f -name "*.php" ! -path "*/node_modules/*" ! -path "*/vendor/*") - - for file in $files; do - # Count the lines of code in the file and add it to the line count - lines=$(wc -l < "$file") - line_count=$((line_count + lines)) - done -} - -# Call the function to count lines of code -count_lines "$directory" - -# Format the line count -if (( line_count >= 1000000 )); then - formatted_count=$(printf "%.2fM" "$(bc -l <<< "$line_count/1000000")") -elif (( line_count >= 1000 )); then - formatted_count=$(printf "%.2fK" "$(bc -l <<< "$line_count/1000")") -else - formatted_count=$line_count -fi - -# Print the total line count -echo "Total lines of code: $formatted_count" diff --git a/devtools/fix_names.bash b/devtools/fix_names.bash deleted file mode 100755 index da42ee2..0000000 --- a/devtools/fix_names.bash +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Function to replace spaces and colons in filenames -rename_files() { - for file in $(find . -type f | sort -r); do - if [[ -f "$file" ]]; then - # Replace spaces with underscores - new_name="${file// /_}" - # Replace colons with %3A - new_name="${new_name//:/%3A}" - # Rename the file - if [[ "$file" != "$new_name" ]]; then - mv "$file" "$new_name" - echo "Renamed '$file' to '$new_name'" - fi - fi - done -} - -# Call the function -rename_files diff --git a/devtools/lint.bash b/devtools/lint.bash deleted file mode 100755 index cb00878..0000000 --- a/devtools/lint.bash +++ /dev/null @@ -1 +0,0 @@ -vendor/bin/php-cs-fixer fix . \ No newline at end of file diff --git a/devtools/push.bash b/devtools/push.bash deleted file mode 100755 index b42f1a8..0000000 --- a/devtools/push.bash +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -composer update -#composer run tests - -if [ $? -ne 0 ]; then - echo "Error occurred during tests. Stopping the script." - exit 1 -fi - -if [ -f "settings.json" ]; then - mv settings.json production_settings.json -fi - - -echo '{ - "__last_updated": "-", - "framework": { - "version": "1.0.1", - "branch": "develop", - "debug": "false", - "name": "MythicalFramework" - }, - "database": { - "host": "127.0.0.1", - "port": "3306", - "username": "", - "password": "", - "name": "framework" - }, - "encryption": { - "method": "MythicalCore", - "key": "" - } - }' > settings.json -echo "This is the first install file ;)" > FIRST_INSTALL diff --git a/devtools/restore.bash b/devtools/restore.bash deleted file mode 100755 index 831fbca..0000000 --- a/devtools/restore.bash +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -if [ -f "production_settings.json" ]; then - rm settings.json - mv production_settings.json settings.json -fi - -rm FIRST_INSTALL \ No newline at end of file diff --git a/devtools/setup_permissions.bash b/devtools/setup_permissions.bash deleted file mode 100755 index 9aed398..0000000 --- a/devtools/setup_permissions.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -chmod -R a+rwx ./* -chmod -R 7777 ./ \ No newline at end of file diff --git a/docs/api/system/.gitkeep b/docs/api/system/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/docs/api/user/.gitkeep b/docs/api/user/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/docs/api/user/register.apit b/docs/api/user/register.apit deleted file mode 100755 index 19959e1..0000000 --- a/docs/api/user/register.apit +++ /dev/null @@ -1,64 +0,0 @@ -{ - "url": "https://framework.mythicalsystems.xyz/api/user/register", - "method": "post", - "data": { - "username": "NaysKutzu", - "email": "ghermancassian2008@gmail.com", - "password": "test", - "first_name": "Cassian", - "last_name": "Gherman" - }, - "backup": { - "bodyType": "form-data", - "data": {}, - "dataArray": [ - [ - "1df925bc-fd84-464d-a4ff-8da9f7c9b3f3", - "username", - "NaysKutzu", - true, - "STRING" - ], - [ - "d0bbf490-fbc1-4273-a3e0-4070cf108c9c", - "email", - "ghermancassian2008@gmail.com", - true, - "STRING" - ], - [ - "484d9a13-e5bc-415a-9fd0-ae2c73f876f8", - "password", - "test", - true, - "STRING" - ], - [ - "aa1dc417-12cf-4826-ae5b-1253f2070b56", - "first_name", - "Cassian", - true, - "STRING" - ], - [ - "3c3005b9-6bb2-4ef8-b8bb-12aae6bc6891", - "last_name", - "Gherman", - true, - "STRING" - ] - ] - }, - "headers": { - "Content-Type": "multipart/form-data" - }, - "headersArray": [ - [ - "b1ce53cc-41fc-463a-975d-9b63374e2b56", - "Content-Type", - "multipart/form-data", - true, - "STRING" - ] - ] -} \ No newline at end of file diff --git a/framework b/framework index ddda12d..f70bbb0 100755 --- a/framework +++ b/framework @@ -11,8 +11,17 @@ try { use MythicalSystemsFramework\Cli\Kernel; +if (function_exists('exec')) { + define('IS_EXEC_ENABLED', true); +} else { + define('IS_EXEC_ENABLED', false); + echo Kernel::translateColorsCode("&cE&0&lr&2&lr&3&lo&5&lr: &r&lThe exec function is disabled, please enable it in your php.ini file"); +} + + try { - Kernel::executeFrameworkCommand(isset($argv[1]) ? $argv[1] : ''); + $args = array_slice($argv, 1); // Exclude the command name and the first argument + Kernel::executeFrameworkCommand(isset($argv[1]) ? $argv[1] : '', $args); } catch (Exception $e) { die(Kernel::translateColorsCode("&cE&0&lr&2&lr&3&lo&5&lr: &r&l" . $e->getMessage() . "")); } \ No newline at end of file diff --git a/install/install.php b/install/install.php index 3959452..798a268 100755 --- a/install/install.php +++ b/install/install.php @@ -2,9 +2,7 @@ use MythicalSystemsFramework\Database\MySQL; use MythicalSystemsFramework\Managers\ConfigManager; -use MythicalSystemsFramework\Database\exception\database\MySQLError; use MythicalSystemsFramework\Managers\DBSettingsManager as settings; -use MythicalSystemsFramework\Database\exception\migration\NoMigrationsFound; ini_set('display_errors', 0); ini_set('display_startup_errors', 0); @@ -62,10 +60,6 @@ try { try { MySQL::migrate(); - } catch (MySQLError $e) { - exit('Failed to migrate the database: ' . $e->getMessage()); - } catch (NoMigrationsFound $e) { - exit('No migrations found!'); } catch (Exception $e) { exit('Failed to migrate the database: ' . $e->getMessage()); } diff --git a/logs/.gitkeep b/logs/.gitkeep deleted file mode 100755 index e69de29..0000000 diff --git a/package.json b/package.json index cd30155..1ee1e84 100755 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "tailwindcss": "^3.4.4" }, "scripts": { - "watch": "npx tailwindcss -i styles.css -o ./public/style.css --watch", - "build": "npx tailwindcss -i styles.css -o ./public/style.css --minify --jit", + "watch": "npx tailwindcss -i ./storage/styles.css -o ./public/style.css --watch", + "build": "npx tailwindcss -i ./storage/styles.css -o ./public/style.css --minify --jit", "clean": "rm -rf ./public/theme.css", "tests": "composer run tests", "install:dependency": "npm i && composer install" diff --git a/public/MythicalFramework.js b/public/MythicalFramework.js new file mode 100755 index 0000000..5738365 --- /dev/null +++ b/public/MythicalFramework.js @@ -0,0 +1,276 @@ +/** + * MythicalFramework.js + * + * This file is the entry point for the MythicalFramework frontend. + */ + +// This value will enable the inspect element thing! +// If you want to disable it, set it to false. +const isDebugMode = true; +// This value will show a warning when debug mode is enabled. +const showDebugWarning = false; + +if (isDebugMode == true && showDebugWarning == true) { + console.warn( + 'Debug mode is enabled. This should only be enabled for development purposes. Do not enable this in production environments.' + ); + Swal.fire({ + title: 'Debug Mode Enabled', + text: 'Debug mode is enabled. This should only be enabled for development purposes. Do not enable this in production environments.', + icon: 'warning', + confirmButtonText: 'OK', + }); +} + +/** + * Warn users about the danger of pasting something in the browser console. + */ +if (window.console && window.console.log) { + setInterval(function () { + console.log('%cSTOP!', 'color: red; font-size: 50px; font-weight: bold; text-shadow: 1px 1px black;'); + console.log('%cPlease do not paste any code into the console.', 'color: red; font-size: 20px;'); + console.log( + '%cPasting code into the console can compromise your account and result in suspension or hacking.', + 'color: red; font-size: 14px;' + ); + }, Math.floor(Math.random() * 4000) + 1000); +} + +/** + * MythicalFramework Dialogs + */ + +document.addEventListener('keydown', function (event) { + if (event.ctrlKey && event.keyCode === 68) { + event.preventDefault(); + Swal.fire({ + title: 'Enter Dialog Number', + input: 'text', + inputAttributes: { + autocapitalize: 'off', + }, + icon: 'question', + text: 'Enter the number of the dialog you would like to open.', + showCancelButton: true, + confirmButtonText: 'Go', + showLoaderOnConfirm: true, + preConfirm: (userInput) => { + if (userInput === null || userInput.trim() === '') { + window.location.href = '/dashboard'; + } else { + switch (userInput) { + case '1': + window.location.href = '/dashboard'; + break; + default: + Swal.fire({ + title: 'Invalid Dialog', + text: 'The dialog number you entered is invalid. Please try again.', + icon: 'error', + confirmButtonText: 'OK', + }); + } + } + }, + }); + } +}); + +/** + * MythicalGuard Blocker + * + * This script checks if the current website is blocked by MythicalGuard. + * + * If the website is blocked, the user will be alerted and the page will be cleared. + */ +const websiteUrl = window.location.hostname; +const blockedWebsitesYamlUrl = 'https://raw.githubusercontent.com/MythicalLTD/MythicalGuard/main/blocked.yml'; + +fetch(blockedWebsitesYamlUrl) + .then((response) => response.text()) + .then((yamlText) => jsyaml.load(yamlText)) + .then((blockedWebsites) => { + const blockedWebsite = blockedWebsites.find((site) => site.url === websiteUrl); + if (blockedWebsite) { + console.error(`${websiteUrl} is blocked for using the service. Reason: ${blockedWebsite.reason}`); + Swal.fire({ + title: 'Website Blocked', + text: `This website (${websiteUrl}) is blocked for using the service. Reason: ${blockedWebsite.reason}`, + icon: 'error', + confirmButtonText: 'OK', + }); + document.write(''); + } else { + console.log(`${websiteUrl} is not blocked for using the service.`); + } + }) + .catch((error) => console.error(error)); + +/** + * Prevent users from using the browser console. + */ +if (isDebugMode == false) { + // take body to change the content + const body = document.getElementsByTagName('body'); + // stop keyboard shortcuts + window.addEventListener('keydown', (event) => { + if (event.ctrlKey && (event.key === 'S' || event.key === 's')) { + event.preventDefault(); + document.getElementById('search-box').focus(); + } + + if (event.ctrlKey && event.key === 'C') { + event.preventDefault(); + showInspectBlockedAlert(); + } + if (event.ctrlKey && (event.key === 'E' || event.key === 'e')) { + event.preventDefault(); + showInspectBlockedAlert(); + } + if (event.ctrlKey && (event.key === 'I' || event.key === 'i')) { + event.preventDefault(); + showInspectBlockedAlert(); + } + if (event.ctrlKey && (event.key === 'K' || event.key === 'k')) { + event.preventDefault(); + showInspectBlockedAlert(); + } + if (event.ctrlKey && (event.key === 'U' || event.key === 'u')) { + event.preventDefault(); + showInspectBlockedAlert(); + } + }); + + function showInspectBlockedAlert() { + Swal.fire({ + title: 'This action is blocked', + text: 'We have blocked this to protect your account and to prevent malicious use.', + icon: 'error', + confirmButtonText: 'OK', + }); + } +} + +/** + * Search functionality + */ +window.addEventListener('keydown', (event) => { + if (event.ctrlKey && (event.key === 'S' || event.key === 's')) { + event.preventDefault(); + document.getElementById('search-box').focus(); + } +}); + +/** + * MythicalFramework Utilities + * + * This script has some utilities that can be used in the frontend. + */ + +/** + * Alert helpers + */ + +/** + * Show a success alert + * + * @param {string} title + * @param {string} text + */ +function showSuccessAlert(title, text) { + Swal.fire({ + title: title, + text: text, + icon: 'success', + confirmButtonText: 'OK', + }); +} +/** + * Show an error alert + * + * @param {string} title + * @param {string} text + */ +function showErrorAlert(title, text) { + Swal.fire({ + title: title, + text: text, + icon: 'error', + confirmButtonText: 'OK', + }); +} +/** + * Show a warning + * + * @param {string} title + * @param {string} text + */ +function showWarningAlert(title, text) { + Swal.fire({ + title: title, + text: text, + icon: 'warning', + confirmButtonText: 'OK', + }); +} + +/** + * Show a confirmation dialog + * + * @param {string} title + * @param {string} text + * @param {string} confirmButtonText + * @param {string} cancelButtonText + * + * @return {boolean} + */ +function showDialogAction(title, text, confirmButtonText, cancelButtonText) { + Swal.fire({ + title: title, + text: text, + showCancelButton: true, + confirmButtonText: confirmButtonText, + cancelButtonText: cancelButtonText, + }).then((result) => { + if (result.isConfirmed) { + return true; + } else { + return false; + } + }); +} + +/** + * Show a confirm action with a text input! + * + * @param {string} title + * @param {string} text + * @param {string} confirmButtonText + * @param {string} cancelButtonText + * + * @return {string} + */ +function showDialogWithInput(title, text, confirmButtonText, cancelButtonText) { + Swal.fire({ + title: title, + text: text, + input: 'text', + inputAttributes: { + autocapitalize: 'off', + }, + showCancelButton: true, + confirmButtonText: confirmButtonText, + cancelButtonText: cancelButtonText, + }).then((result) => { + if (result.isConfirmed) { + if (result.value === null || result.value.trim() === '') { + return ""; + } else { + return result.value; + } + } else { + return ""; + } + }); +} + diff --git a/public/index.php b/public/index.php index 846e99d..a0944f9 100755 --- a/public/index.php +++ b/public/index.php @@ -11,16 +11,21 @@ } use Twig\Environment; +use MythicalSystemsFramework\App; use Twig\Loader\FilesystemLoader; use MythicalSystems\Api\Api as api; use MythicalSystems\Api\ResponseHandler as rsp; -use MythicalSystemsFramework\Firewall\Firewall; use MythicalSystemsFramework\Managers\Settings; use MythicalSystemsFramework\Managers\LanguageManager; use MythicalSystemsFramework\Managers\ConfigManager as cfg; $router = new Router\Router(); -Firewall::check(Firewall::getUserIP()); + +if (function_exists('exec')) { + define('IS_EXEC_ENABLED', true); +} else { + define('IS_EXEC_ENABLED', false); +} /* * Check if the app is installed @@ -53,14 +58,14 @@ exit('We have no access to the framework directory!'); } -if (!is_writable(__DIR__ . '/../caches')) { +if (!is_writable(__DIR__ . '/../storage/caches')) { exit('We have no access to the cache directory!'); } date_default_timezone_set(Settings::getSetting('app', 'timezone')); -define('DIR_TEMPLATE', __DIR__ . '/../themes/' . Settings::getSetting('app', 'theme')); -define('DIR_CACHE', __DIR__ . '/../caches'); +define('DIR_TEMPLATE', __DIR__ . '/../storage/themes/' . Settings::getSetting('app', 'theme')); +define('DIR_CACHE', __DIR__ . '/../storage/caches'); define('TIMEZONE', Settings::getSetting('app', 'timezone')); /* * Load the template engine @@ -78,6 +83,7 @@ 'cache' => DIR_CACHE, 'auto_reload' => true, 'debug' => true, + 'charset' => 'utf-8', ]); $renderer->addExtension(new Twig\Extension\DebugExtension()); /* @@ -102,6 +108,7 @@ })); $renderer->addGlobal('php_version', phpversion()); + $renderer->addGlobal('page_name', 'Home'); define('VIEW_ENGINE', $renderer); diff --git a/public/third_party/js-yaml.js b/public/third_party/js-yaml.js new file mode 100755 index 0000000..696e9c8 --- /dev/null +++ b/public/third_party/js-yaml.js @@ -0,0 +1,4012 @@ +/*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? factory(exports) + : typeof define === 'function' && define.amd + ? define(['exports'], factory) + : ((global = typeof globalThis !== 'undefined' ? globalThis : global || self), factory((global.jsyaml = {}))); +})(this, function (exports) { + 'use strict'; + + function isNothing(subject) { + return typeof subject === 'undefined' || subject === null; + } + + function isObject(subject) { + return typeof subject === 'object' && subject !== null; + } + + function toArray(sequence) { + if (Array.isArray(sequence)) return sequence; + else if (isNothing(sequence)) return []; + + return [sequence]; + } + + function extend(target, source) { + var index, length, key, sourceKeys; + + if (source) { + sourceKeys = Object.keys(source); + + for (index = 0, length = sourceKeys.length; index < length; index += 1) { + key = sourceKeys[index]; + target[key] = source[key]; + } + } + + return target; + } + + function repeat(string, count) { + var result = '', + cycle; + + for (cycle = 0; cycle < count; cycle += 1) { + result += string; + } + + return result; + } + + function isNegativeZero(number) { + return number === 0 && Number.NEGATIVE_INFINITY === 1 / number; + } + + var isNothing_1 = isNothing; + var isObject_1 = isObject; + var toArray_1 = toArray; + var repeat_1 = repeat; + var isNegativeZero_1 = isNegativeZero; + var extend_1 = extend; + + var common = { + isNothing: isNothing_1, + isObject: isObject_1, + toArray: toArray_1, + repeat: repeat_1, + isNegativeZero: isNegativeZero_1, + extend: extend_1, + }; + + // YAML error class. http://stackoverflow.com/questions/8458984 + + function formatError(exception, compact) { + var where = '', + message = exception.reason || '(unknown reason)'; + + if (!exception.mark) return message; + + if (exception.mark.name) { + where += 'in "' + exception.mark.name + '" '; + } + + where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')'; + + if (!compact && exception.mark.snippet) { + where += '\n\n' + exception.mark.snippet; + } + + return message + ' ' + where; + } + + function YAMLException$1(reason, mark) { + // Super constructor + Error.call(this); + + this.name = 'YAMLException'; + this.reason = reason; + this.mark = mark; + this.message = formatError(this, false); + + // Include stack trace in error object + if (Error.captureStackTrace) { + // Chrome and NodeJS + Error.captureStackTrace(this, this.constructor); + } else { + // FF, IE 10+ and Safari 6+. Fallback for others + this.stack = new Error().stack || ''; + } + } + + // Inherit from Error + YAMLException$1.prototype = Object.create(Error.prototype); + YAMLException$1.prototype.constructor = YAMLException$1; + + YAMLException$1.prototype.toString = function toString(compact) { + return this.name + ': ' + formatError(this, compact); + }; + + var exception = YAMLException$1; + + // get snippet for a single line, respecting maxLength + function getLine(buffer, lineStart, lineEnd, position, maxLineLength) { + var head = ''; + var tail = ''; + var maxHalfLength = Math.floor(maxLineLength / 2) - 1; + + if (position - lineStart > maxHalfLength) { + head = ' ... '; + lineStart = position - maxHalfLength + head.length; + } + + if (lineEnd - position > maxHalfLength) { + tail = ' ...'; + lineEnd = position + maxHalfLength - tail.length; + } + + return { + str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, '→') + tail, + pos: position - lineStart + head.length, // relative position + }; + } + + function padStart(string, max) { + return common.repeat(' ', max - string.length) + string; + } + + function makeSnippet(mark, options) { + options = Object.create(options || null); + + if (!mark.buffer) return null; + + if (!options.maxLength) options.maxLength = 79; + if (typeof options.indent !== 'number') options.indent = 1; + if (typeof options.linesBefore !== 'number') options.linesBefore = 3; + if (typeof options.linesAfter !== 'number') options.linesAfter = 2; + + var re = /\r?\n|\r|\0/g; + var lineStarts = [0]; + var lineEnds = []; + var match; + var foundLineNo = -1; + + while ((match = re.exec(mark.buffer))) { + lineEnds.push(match.index); + lineStarts.push(match.index + match[0].length); + + if (mark.position <= match.index && foundLineNo < 0) { + foundLineNo = lineStarts.length - 2; + } + } + + if (foundLineNo < 0) foundLineNo = lineStarts.length - 1; + + var result = '', + i, + line; + var lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length; + var maxLineLength = options.maxLength - (options.indent + lineNoLength + 3); + + for (i = 1; i <= options.linesBefore; i++) { + if (foundLineNo - i < 0) break; + line = getLine( + mark.buffer, + lineStarts[foundLineNo - i], + lineEnds[foundLineNo - i], + mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]), + maxLineLength + ); + result = + common.repeat(' ', options.indent) + + padStart((mark.line - i + 1).toString(), lineNoLength) + + ' | ' + + line.str + + '\n' + + result; + } + + line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength); + result += + common.repeat(' ', options.indent) + + padStart((mark.line + 1).toString(), lineNoLength) + + ' | ' + + line.str + + '\n'; + result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\n'; + + for (i = 1; i <= options.linesAfter; i++) { + if (foundLineNo + i >= lineEnds.length) break; + line = getLine( + mark.buffer, + lineStarts[foundLineNo + i], + lineEnds[foundLineNo + i], + mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]), + maxLineLength + ); + result += + common.repeat(' ', options.indent) + + padStart((mark.line + i + 1).toString(), lineNoLength) + + ' | ' + + line.str + + '\n'; + } + + return result.replace(/\n$/, ''); + } + + var snippet = makeSnippet; + + var TYPE_CONSTRUCTOR_OPTIONS = [ + 'kind', + 'multi', + 'resolve', + 'construct', + 'instanceOf', + 'predicate', + 'represent', + 'representName', + 'defaultStyle', + 'styleAliases', + ]; + + var YAML_NODE_KINDS = ['scalar', 'sequence', 'mapping']; + + function compileStyleAliases(map) { + var result = {}; + + if (map !== null) { + Object.keys(map).forEach(function (style) { + map[style].forEach(function (alias) { + result[String(alias)] = style; + }); + }); + } + + return result; + } + + function Type$1(tag, options) { + options = options || {}; + + Object.keys(options).forEach(function (name) { + if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { + throw new exception('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); + } + }); + + // TODO: Add tag format check. + this.options = options; // keep original options in case user wants to extend this type later + this.tag = tag; + this.kind = options['kind'] || null; + this.resolve = + options['resolve'] || + function () { + return true; + }; + this.construct = + options['construct'] || + function (data) { + return data; + }; + this.instanceOf = options['instanceOf'] || null; + this.predicate = options['predicate'] || null; + this.represent = options['represent'] || null; + this.representName = options['representName'] || null; + this.defaultStyle = options['defaultStyle'] || null; + this.multi = options['multi'] || false; + this.styleAliases = compileStyleAliases(options['styleAliases'] || null); + + if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { + throw new exception('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); + } + } + + var type = Type$1; + + /*eslint-disable max-len*/ + + function compileList(schema, name) { + var result = []; + + schema[name].forEach(function (currentType) { + var newIndex = result.length; + + result.forEach(function (previousType, previousIndex) { + if ( + previousType.tag === currentType.tag && + previousType.kind === currentType.kind && + previousType.multi === currentType.multi + ) { + newIndex = previousIndex; + } + }); + + result[newIndex] = currentType; + }); + + return result; + } + + function compileMap(/* lists... */) { + var result = { + scalar: {}, + sequence: {}, + mapping: {}, + fallback: {}, + multi: { + scalar: [], + sequence: [], + mapping: [], + fallback: [], + }, + }, + index, + length; + + function collectType(type) { + if (type.multi) { + result.multi[type.kind].push(type); + result.multi['fallback'].push(type); + } else { + result[type.kind][type.tag] = result['fallback'][type.tag] = type; + } + } + + for (index = 0, length = arguments.length; index < length; index += 1) { + arguments[index].forEach(collectType); + } + return result; + } + + function Schema$1(definition) { + return this.extend(definition); + } + + Schema$1.prototype.extend = function extend(definition) { + var implicit = []; + var explicit = []; + + if (definition instanceof type) { + // Schema.extend(type) + explicit.push(definition); + } else if (Array.isArray(definition)) { + // Schema.extend([ type1, type2, ... ]) + explicit = explicit.concat(definition); + } else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) { + // Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] }) + if (definition.implicit) implicit = implicit.concat(definition.implicit); + if (definition.explicit) explicit = explicit.concat(definition.explicit); + } else { + throw new exception( + 'Schema.extend argument should be a Type, [ Type ], ' + + 'or a schema definition ({ implicit: [...], explicit: [...] })' + ); + } + + implicit.forEach(function (type$1) { + if (!(type$1 instanceof type)) { + throw new exception( + 'Specified list of YAML types (or a single Type object) contains a non-Type object.' + ); + } + + if (type$1.loadKind && type$1.loadKind !== 'scalar') { + throw new exception( + 'There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.' + ); + } + + if (type$1.multi) { + throw new exception( + 'There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.' + ); + } + }); + + explicit.forEach(function (type$1) { + if (!(type$1 instanceof type)) { + throw new exception( + 'Specified list of YAML types (or a single Type object) contains a non-Type object.' + ); + } + }); + + var result = Object.create(Schema$1.prototype); + + result.implicit = (this.implicit || []).concat(implicit); + result.explicit = (this.explicit || []).concat(explicit); + + result.compiledImplicit = compileList(result, 'implicit'); + result.compiledExplicit = compileList(result, 'explicit'); + result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit); + + return result; + }; + + var schema = Schema$1; + + var str = new type('tag:yaml.org,2002:str', { + kind: 'scalar', + construct: function (data) { + return data !== null ? data : ''; + }, + }); + + var seq = new type('tag:yaml.org,2002:seq', { + kind: 'sequence', + construct: function (data) { + return data !== null ? data : []; + }, + }); + + var map = new type('tag:yaml.org,2002:map', { + kind: 'mapping', + construct: function (data) { + return data !== null ? data : {}; + }, + }); + + var failsafe = new schema({ + explicit: [str, seq, map], + }); + + function resolveYamlNull(data) { + if (data === null) return true; + + var max = data.length; + + return (max === 1 && data === '~') || (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL')); + } + + function constructYamlNull() { + return null; + } + + function isNull(object) { + return object === null; + } + + var _null = new type('tag:yaml.org,2002:null', { + kind: 'scalar', + resolve: resolveYamlNull, + construct: constructYamlNull, + predicate: isNull, + represent: { + canonical: function () { + return '~'; + }, + lowercase: function () { + return 'null'; + }, + uppercase: function () { + return 'NULL'; + }, + camelcase: function () { + return 'Null'; + }, + empty: function () { + return ''; + }, + }, + defaultStyle: 'lowercase', + }); + + function resolveYamlBoolean(data) { + if (data === null) return false; + + var max = data.length; + + return ( + (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) || + (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE')) + ); + } + + function constructYamlBoolean(data) { + return data === 'true' || data === 'True' || data === 'TRUE'; + } + + function isBoolean(object) { + return Object.prototype.toString.call(object) === '[object Boolean]'; + } + + var bool = new type('tag:yaml.org,2002:bool', { + kind: 'scalar', + resolve: resolveYamlBoolean, + construct: constructYamlBoolean, + predicate: isBoolean, + represent: { + lowercase: function (object) { + return object ? 'true' : 'false'; + }, + uppercase: function (object) { + return object ? 'TRUE' : 'FALSE'; + }, + camelcase: function (object) { + return object ? 'True' : 'False'; + }, + }, + defaultStyle: 'lowercase', + }); + + function isHexCode(c) { + return ( + (0x30 /* 0 */ <= c && c <= 0x39 /* 9 */) || + (0x41 /* A */ <= c && c <= 0x46 /* F */) || + (0x61 /* a */ <= c && c <= 0x66 /* f */) + ); + } + + function isOctCode(c) { + return 0x30 /* 0 */ <= c && c <= 0x37 /* 7 */; + } + + function isDecCode(c) { + return 0x30 /* 0 */ <= c && c <= 0x39 /* 9 */; + } + + function resolveYamlInteger(data) { + if (data === null) return false; + + var max = data.length, + index = 0, + hasDigits = false, + ch; + + if (!max) return false; + + ch = data[index]; + + // sign + if (ch === '-' || ch === '+') { + ch = data[++index]; + } + + if (ch === '0') { + // 0 + if (index + 1 === max) return true; + ch = data[++index]; + + // base 2, base 8, base 16 + + if (ch === 'b') { + // base 2 + index++; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (ch !== '0' && ch !== '1') return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + + if (ch === 'x') { + // base 16 + index++; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (!isHexCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + + if (ch === 'o') { + // base 8 + index++; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (!isOctCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + } + + // base 10 (except 0) + + // value should not start with `_`; + if (ch === '_') return false; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (!isDecCode(data.charCodeAt(index))) { + return false; + } + hasDigits = true; + } + + // Should have digits and should not end with `_` + if (!hasDigits || ch === '_') return false; + + return true; + } + + function constructYamlInteger(data) { + var value = data, + sign = 1, + ch; + + if (value.indexOf('_') !== -1) { + value = value.replace(/_/g, ''); + } + + ch = value[0]; + + if (ch === '-' || ch === '+') { + if (ch === '-') sign = -1; + value = value.slice(1); + ch = value[0]; + } + + if (value === '0') return 0; + + if (ch === '0') { + if (value[1] === 'b') return sign * parseInt(value.slice(2), 2); + if (value[1] === 'x') return sign * parseInt(value.slice(2), 16); + if (value[1] === 'o') return sign * parseInt(value.slice(2), 8); + } + + return sign * parseInt(value, 10); + } + + function isInteger(object) { + return ( + Object.prototype.toString.call(object) === '[object Number]' && + object % 1 === 0 && + !common.isNegativeZero(object) + ); + } + + var int = new type('tag:yaml.org,2002:int', { + kind: 'scalar', + resolve: resolveYamlInteger, + construct: constructYamlInteger, + predicate: isInteger, + represent: { + binary: function (obj) { + return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); + }, + octal: function (obj) { + return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1); + }, + decimal: function (obj) { + return obj.toString(10); + }, + /* eslint-disable max-len */ + hexadecimal: function (obj) { + return obj >= 0 + ? '0x' + obj.toString(16).toUpperCase() + : '-0x' + obj.toString(16).toUpperCase().slice(1); + }, + }, + defaultStyle: 'decimal', + styleAliases: { + binary: [2, 'bin'], + octal: [8, 'oct'], + decimal: [10, 'dec'], + hexadecimal: [16, 'hex'], + }, + }); + + var YAML_FLOAT_PATTERN = new RegExp( + // 2.5e4, 2.5 and integers + '^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' + + // .2e4, .2 + // special case, seems not from spec + '|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' + + // .inf + '|[-+]?\\.(?:inf|Inf|INF)' + + // .nan + '|\\.(?:nan|NaN|NAN))$' + ); + + function resolveYamlFloat(data) { + if (data === null) return false; + + if ( + !YAML_FLOAT_PATTERN.test(data) || + // Quick hack to not allow integers end with `_` + // Probably should update regexp & check speed + data[data.length - 1] === '_' + ) { + return false; + } + + return true; + } + + function constructYamlFloat(data) { + var value, sign; + + value = data.replace(/_/g, '').toLowerCase(); + sign = value[0] === '-' ? -1 : 1; + + if ('+-'.indexOf(value[0]) >= 0) { + value = value.slice(1); + } + + if (value === '.inf') { + return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; + } else if (value === '.nan') { + return NaN; + } + return sign * parseFloat(value, 10); + } + + var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/; + + function representYamlFloat(object, style) { + var res; + + if (isNaN(object)) { + switch (style) { + case 'lowercase': + return '.nan'; + case 'uppercase': + return '.NAN'; + case 'camelcase': + return '.NaN'; + } + } else if (Number.POSITIVE_INFINITY === object) { + switch (style) { + case 'lowercase': + return '.inf'; + case 'uppercase': + return '.INF'; + case 'camelcase': + return '.Inf'; + } + } else if (Number.NEGATIVE_INFINITY === object) { + switch (style) { + case 'lowercase': + return '-.inf'; + case 'uppercase': + return '-.INF'; + case 'camelcase': + return '-.Inf'; + } + } else if (common.isNegativeZero(object)) { + return '-0.0'; + } + + res = object.toString(10); + + // JS stringifier can build scientific format without dots: 5e-100, + // while YAML requres dot: 5.e-100. Fix it with simple hack + + return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res; + } + + function isFloat(object) { + return ( + Object.prototype.toString.call(object) === '[object Number]' && + (object % 1 !== 0 || common.isNegativeZero(object)) + ); + } + + var float = new type('tag:yaml.org,2002:float', { + kind: 'scalar', + resolve: resolveYamlFloat, + construct: constructYamlFloat, + predicate: isFloat, + represent: representYamlFloat, + defaultStyle: 'lowercase', + }); + + var json = failsafe.extend({ + implicit: [_null, bool, int, float], + }); + + var core = json; + + var YAML_DATE_REGEXP = new RegExp( + '^([0-9][0-9][0-9][0-9])' + // [1] year + '-([0-9][0-9])' + // [2] month + '-([0-9][0-9])$' + ); // [3] day + + var YAML_TIMESTAMP_REGEXP = new RegExp( + '^([0-9][0-9][0-9][0-9])' + // [1] year + '-([0-9][0-9]?)' + // [2] month + '-([0-9][0-9]?)' + // [3] day + '(?:[Tt]|[ \\t]+)' + // ... + '([0-9][0-9]?)' + // [4] hour + ':([0-9][0-9])' + // [5] minute + ':([0-9][0-9])' + // [6] second + '(?:\\.([0-9]*))?' + // [7] fraction + '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour + '(?::([0-9][0-9]))?))?$' + ); // [11] tz_minute + + function resolveYamlTimestamp(data) { + if (data === null) return false; + if (YAML_DATE_REGEXP.exec(data) !== null) return true; + if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true; + return false; + } + + function constructYamlTimestamp(data) { + var match, + year, + month, + day, + hour, + minute, + second, + fraction = 0, + delta = null, + tz_hour, + tz_minute, + date; + + match = YAML_DATE_REGEXP.exec(data); + if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data); + + if (match === null) throw new Error('Date resolve error'); + + // match: [1] year [2] month [3] day + + year = +match[1]; + month = +match[2] - 1; // JS month starts with 0 + day = +match[3]; + + if (!match[4]) { + // no hour + return new Date(Date.UTC(year, month, day)); + } + + // match: [4] hour [5] minute [6] second [7] fraction + + hour = +match[4]; + minute = +match[5]; + second = +match[6]; + + if (match[7]) { + fraction = match[7].slice(0, 3); + while (fraction.length < 3) { + // milli-seconds + fraction += '0'; + } + fraction = +fraction; + } + + // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute + + if (match[9]) { + tz_hour = +match[10]; + tz_minute = +(match[11] || 0); + delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds + if (match[9] === '-') delta = -delta; + } + + date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); + + if (delta) date.setTime(date.getTime() - delta); + + return date; + } + + function representYamlTimestamp(object /*, style*/) { + return object.toISOString(); + } + + var timestamp = new type('tag:yaml.org,2002:timestamp', { + kind: 'scalar', + resolve: resolveYamlTimestamp, + construct: constructYamlTimestamp, + instanceOf: Date, + represent: representYamlTimestamp, + }); + + function resolveYamlMerge(data) { + return data === '<<' || data === null; + } + + var merge = new type('tag:yaml.org,2002:merge', { + kind: 'scalar', + resolve: resolveYamlMerge, + }); + + /*eslint-disable no-bitwise*/ + + // [ 64, 65, 66 ] -> [ padding, CR, LF ] + var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'; + + function resolveYamlBinary(data) { + if (data === null) return false; + + var code, + idx, + bitlen = 0, + max = data.length, + map = BASE64_MAP; + + // Convert one by one. + for (idx = 0; idx < max; idx++) { + code = map.indexOf(data.charAt(idx)); + + // Skip CR/LF + if (code > 64) continue; + + // Fail on illegal characters + if (code < 0) return false; + + bitlen += 6; + } + + // If there are any bits left, source was corrupted + return bitlen % 8 === 0; + } + + function constructYamlBinary(data) { + var idx, + tailbits, + input = data.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan + max = input.length, + map = BASE64_MAP, + bits = 0, + result = []; + + // Collect by 6*4 bits (3 bytes) + + for (idx = 0; idx < max; idx++) { + if (idx % 4 === 0 && idx) { + result.push((bits >> 16) & 0xff); + result.push((bits >> 8) & 0xff); + result.push(bits & 0xff); + } + + bits = (bits << 6) | map.indexOf(input.charAt(idx)); + } + + // Dump tail + + tailbits = (max % 4) * 6; + + if (tailbits === 0) { + result.push((bits >> 16) & 0xff); + result.push((bits >> 8) & 0xff); + result.push(bits & 0xff); + } else if (tailbits === 18) { + result.push((bits >> 10) & 0xff); + result.push((bits >> 2) & 0xff); + } else if (tailbits === 12) { + result.push((bits >> 4) & 0xff); + } + + return new Uint8Array(result); + } + + function representYamlBinary(object /*, style*/) { + var result = '', + bits = 0, + idx, + tail, + max = object.length, + map = BASE64_MAP; + + // Convert every three bytes to 4 ASCII characters. + + for (idx = 0; idx < max; idx++) { + if (idx % 3 === 0 && idx) { + result += map[(bits >> 18) & 0x3f]; + result += map[(bits >> 12) & 0x3f]; + result += map[(bits >> 6) & 0x3f]; + result += map[bits & 0x3f]; + } + + bits = (bits << 8) + object[idx]; + } + + // Dump tail + + tail = max % 3; + + if (tail === 0) { + result += map[(bits >> 18) & 0x3f]; + result += map[(bits >> 12) & 0x3f]; + result += map[(bits >> 6) & 0x3f]; + result += map[bits & 0x3f]; + } else if (tail === 2) { + result += map[(bits >> 10) & 0x3f]; + result += map[(bits >> 4) & 0x3f]; + result += map[(bits << 2) & 0x3f]; + result += map[64]; + } else if (tail === 1) { + result += map[(bits >> 2) & 0x3f]; + result += map[(bits << 4) & 0x3f]; + result += map[64]; + result += map[64]; + } + + return result; + } + + function isBinary(obj) { + return Object.prototype.toString.call(obj) === '[object Uint8Array]'; + } + + var binary = new type('tag:yaml.org,2002:binary', { + kind: 'scalar', + resolve: resolveYamlBinary, + construct: constructYamlBinary, + predicate: isBinary, + represent: representYamlBinary, + }); + + var _hasOwnProperty$3 = Object.prototype.hasOwnProperty; + var _toString$2 = Object.prototype.toString; + + function resolveYamlOmap(data) { + if (data === null) return true; + + var objectKeys = [], + index, + length, + pair, + pairKey, + pairHasKey, + object = data; + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + pairHasKey = false; + + if (_toString$2.call(pair) !== '[object Object]') return false; + + for (pairKey in pair) { + if (_hasOwnProperty$3.call(pair, pairKey)) { + if (!pairHasKey) pairHasKey = true; + else return false; + } + } + + if (!pairHasKey) return false; + + if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey); + else return false; + } + + return true; + } + + function constructYamlOmap(data) { + return data !== null ? data : []; + } + + var omap = new type('tag:yaml.org,2002:omap', { + kind: 'sequence', + resolve: resolveYamlOmap, + construct: constructYamlOmap, + }); + + var _toString$1 = Object.prototype.toString; + + function resolveYamlPairs(data) { + if (data === null) return true; + + var index, + length, + pair, + keys, + result, + object = data; + + result = new Array(object.length); + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + + if (_toString$1.call(pair) !== '[object Object]') return false; + + keys = Object.keys(pair); + + if (keys.length !== 1) return false; + + result[index] = [keys[0], pair[keys[0]]]; + } + + return true; + } + + function constructYamlPairs(data) { + if (data === null) return []; + + var index, + length, + pair, + keys, + result, + object = data; + + result = new Array(object.length); + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + + keys = Object.keys(pair); + + result[index] = [keys[0], pair[keys[0]]]; + } + + return result; + } + + var pairs = new type('tag:yaml.org,2002:pairs', { + kind: 'sequence', + resolve: resolveYamlPairs, + construct: constructYamlPairs, + }); + + var _hasOwnProperty$2 = Object.prototype.hasOwnProperty; + + function resolveYamlSet(data) { + if (data === null) return true; + + var key, + object = data; + + for (key in object) { + if (_hasOwnProperty$2.call(object, key)) { + if (object[key] !== null) return false; + } + } + + return true; + } + + function constructYamlSet(data) { + return data !== null ? data : {}; + } + + var set = new type('tag:yaml.org,2002:set', { + kind: 'mapping', + resolve: resolveYamlSet, + construct: constructYamlSet, + }); + + var _default = core.extend({ + implicit: [timestamp, merge], + explicit: [binary, omap, pairs, set], + }); + + /*eslint-disable max-len,no-use-before-define*/ + + var _hasOwnProperty$1 = Object.prototype.hasOwnProperty; + + var CONTEXT_FLOW_IN = 1; + var CONTEXT_FLOW_OUT = 2; + var CONTEXT_BLOCK_IN = 3; + var CONTEXT_BLOCK_OUT = 4; + + var CHOMPING_CLIP = 1; + var CHOMPING_STRIP = 2; + var CHOMPING_KEEP = 3; + + var PATTERN_NON_PRINTABLE = + /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; + var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; + var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; + var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; + var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; + + function _class(obj) { + return Object.prototype.toString.call(obj); + } + + function is_EOL(c) { + return c === 0x0a /* LF */ || c === 0x0d /* CR */; + } + + function is_WHITE_SPACE(c) { + return c === 0x09 /* Tab */ || c === 0x20 /* Space */; + } + + function is_WS_OR_EOL(c) { + return c === 0x09 /* Tab */ || c === 0x20 /* Space */ || c === 0x0a /* LF */ || c === 0x0d /* CR */; + } + + function is_FLOW_INDICATOR(c) { + return ( + c === 0x2c /* , */ || c === 0x5b /* [ */ || c === 0x5d /* ] */ || c === 0x7b /* { */ || c === 0x7d /* } */ + ); + } + + function fromHexCode(c) { + var lc; + + if (0x30 /* 0 */ <= c && c <= 0x39 /* 9 */) { + return c - 0x30; + } + + /*eslint-disable no-bitwise*/ + lc = c | 0x20; + + if (0x61 /* a */ <= lc && lc <= 0x66 /* f */) { + return lc - 0x61 + 10; + } + + return -1; + } + + function escapedHexLen(c) { + if (c === 0x78 /* x */) { + return 2; + } + if (c === 0x75 /* u */) { + return 4; + } + if (c === 0x55 /* U */) { + return 8; + } + return 0; + } + + function fromDecimalCode(c) { + if (0x30 /* 0 */ <= c && c <= 0x39 /* 9 */) { + return c - 0x30; + } + + return -1; + } + + function simpleEscapeSequence(c) { + /* eslint-disable indent */ + return c === 0x30 /* 0 */ + ? '\x00' + : c === 0x61 /* a */ + ? '\x07' + : c === 0x62 /* b */ + ? '\x08' + : c === 0x74 /* t */ + ? '\x09' + : c === 0x09 /* Tab */ + ? '\x09' + : c === 0x6e /* n */ + ? '\x0A' + : c === 0x76 /* v */ + ? '\x0B' + : c === 0x66 /* f */ + ? '\x0C' + : c === 0x72 /* r */ + ? '\x0D' + : c === 0x65 /* e */ + ? '\x1B' + : c === 0x20 /* Space */ + ? ' ' + : c === 0x22 /* " */ + ? '\x22' + : c === 0x2f /* / */ + ? '/' + : c === 0x5c /* \ */ + ? '\x5C' + : c === 0x4e /* N */ + ? '\x85' + : c === 0x5f /* _ */ + ? '\xA0' + : c === 0x4c /* L */ + ? '\u2028' + : c === 0x50 /* P */ + ? '\u2029' + : ''; + } + + function charFromCodepoint(c) { + if (c <= 0xffff) { + return String.fromCharCode(c); + } + // Encode UTF-16 surrogate pair + // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF + return String.fromCharCode(((c - 0x010000) >> 10) + 0xd800, ((c - 0x010000) & 0x03ff) + 0xdc00); + } + + var simpleEscapeCheck = new Array(256); // integer, for fast access + var simpleEscapeMap = new Array(256); + for (var i = 0; i < 256; i++) { + simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; + simpleEscapeMap[i] = simpleEscapeSequence(i); + } + + function State$1(input, options) { + this.input = input; + + this.filename = options['filename'] || null; + this.schema = options['schema'] || _default; + this.onWarning = options['onWarning'] || null; + // (Hidden) Remove? makes the loader to expect YAML 1.1 documents + // if such documents have no explicit %YAML directive + this.legacy = options['legacy'] || false; + + this.json = options['json'] || false; + this.listener = options['listener'] || null; + + this.implicitTypes = this.schema.compiledImplicit; + this.typeMap = this.schema.compiledTypeMap; + + this.length = input.length; + this.position = 0; + this.line = 0; + this.lineStart = 0; + this.lineIndent = 0; + + // position of first leading tab in the current line, + // used to make sure there are no tabs in the indentation + this.firstTabInLine = -1; + + this.documents = []; + + /* + this.version; + this.checkLineBreaks; + this.tagMap; + this.anchorMap; + this.tag; + this.anchor; + this.kind; + this.result;*/ + } + + function generateError(state, message) { + var mark = { + name: state.filename, + buffer: state.input.slice(0, -1), // omit trailing \0 + position: state.position, + line: state.line, + column: state.position - state.lineStart, + }; + + mark.snippet = snippet(mark); + + return new exception(message, mark); + } + + function throwError(state, message) { + throw generateError(state, message); + } + + function throwWarning(state, message) { + if (state.onWarning) { + state.onWarning.call(null, generateError(state, message)); + } + } + + var directiveHandlers = { + YAML: function handleYamlDirective(state, name, args) { + var match, major, minor; + + if (state.version !== null) { + throwError(state, 'duplication of %YAML directive'); + } + + if (args.length !== 1) { + throwError(state, 'YAML directive accepts exactly one argument'); + } + + match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); + + if (match === null) { + throwError(state, 'ill-formed argument of the YAML directive'); + } + + major = parseInt(match[1], 10); + minor = parseInt(match[2], 10); + + if (major !== 1) { + throwError(state, 'unacceptable YAML version of the document'); + } + + state.version = args[0]; + state.checkLineBreaks = minor < 2; + + if (minor !== 1 && minor !== 2) { + throwWarning(state, 'unsupported YAML version of the document'); + } + }, + + TAG: function handleTagDirective(state, name, args) { + var handle, prefix; + + if (args.length !== 2) { + throwError(state, 'TAG directive accepts exactly two arguments'); + } + + handle = args[0]; + prefix = args[1]; + + if (!PATTERN_TAG_HANDLE.test(handle)) { + throwError(state, 'ill-formed tag handle (first argument) of the TAG directive'); + } + + if (_hasOwnProperty$1.call(state.tagMap, handle)) { + throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle'); + } + + if (!PATTERN_TAG_URI.test(prefix)) { + throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive'); + } + + try { + prefix = decodeURIComponent(prefix); + } catch (err) { + throwError(state, 'tag prefix is malformed: ' + prefix); + } + + state.tagMap[handle] = prefix; + }, + }; + + function captureSegment(state, start, end, checkJson) { + var _position, _length, _character, _result; + + if (start < end) { + _result = state.input.slice(start, end); + + if (checkJson) { + for (_position = 0, _length = _result.length; _position < _length; _position += 1) { + _character = _result.charCodeAt(_position); + if (!(_character === 0x09 || (0x20 <= _character && _character <= 0x10ffff))) { + throwError(state, 'expected valid JSON character'); + } + } + } else if (PATTERN_NON_PRINTABLE.test(_result)) { + throwError(state, 'the stream contains non-printable characters'); + } + + state.result += _result; + } + } + + function mergeMappings(state, destination, source, overridableKeys) { + var sourceKeys, key, index, quantity; + + if (!common.isObject(source)) { + throwError(state, 'cannot merge mappings; the provided source object is unacceptable'); + } + + sourceKeys = Object.keys(source); + + for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) { + key = sourceKeys[index]; + + if (!_hasOwnProperty$1.call(destination, key)) { + destination[key] = source[key]; + overridableKeys[key] = true; + } + } + } + + function storeMappingPair( + state, + _result, + overridableKeys, + keyTag, + keyNode, + valueNode, + startLine, + startLineStart, + startPos + ) { + var index, quantity; + + // The output is a plain object here, so keys can only be strings. + // We need to convert keyNode to a string, but doing so can hang the process + // (deeply nested arrays that explode exponentially using aliases). + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + + for (index = 0, quantity = keyNode.length; index < quantity; index += 1) { + if (Array.isArray(keyNode[index])) { + throwError(state, 'nested arrays are not supported inside keys'); + } + + if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') { + keyNode[index] = '[object Object]'; + } + } + } + + // Avoid code execution in load() via toString property + // (still use its own toString for arrays, timestamps, + // and whatever user schema extensions happen to have @@toStringTag) + if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') { + keyNode = '[object Object]'; + } + + keyNode = String(keyNode); + + if (_result === null) { + _result = {}; + } + + if (keyTag === 'tag:yaml.org,2002:merge') { + if (Array.isArray(valueNode)) { + for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { + mergeMappings(state, _result, valueNode[index], overridableKeys); + } + } else { + mergeMappings(state, _result, valueNode, overridableKeys); + } + } else { + if ( + !state.json && + !_hasOwnProperty$1.call(overridableKeys, keyNode) && + _hasOwnProperty$1.call(_result, keyNode) + ) { + state.line = startLine || state.line; + state.lineStart = startLineStart || state.lineStart; + state.position = startPos || state.position; + throwError(state, 'duplicated mapping key'); + } + + // used for this specific key only because Object.defineProperty is slow + if (keyNode === '__proto__') { + Object.defineProperty(_result, keyNode, { + configurable: true, + enumerable: true, + writable: true, + value: valueNode, + }); + } else { + _result[keyNode] = valueNode; + } + delete overridableKeys[keyNode]; + } + + return _result; + } + + function readLineBreak(state) { + var ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x0a /* LF */) { + state.position++; + } else if (ch === 0x0d /* CR */) { + state.position++; + if (state.input.charCodeAt(state.position) === 0x0a /* LF */) { + state.position++; + } + } else { + throwError(state, 'a line break is expected'); + } + + state.line += 1; + state.lineStart = state.position; + state.firstTabInLine = -1; + } + + function skipSeparationSpace(state, allowComments, checkIndent) { + var lineBreaks = 0, + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + while (is_WHITE_SPACE(ch)) { + if (ch === 0x09 /* Tab */ && state.firstTabInLine === -1) { + state.firstTabInLine = state.position; + } + ch = state.input.charCodeAt(++state.position); + } + + if (allowComments && ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0x0a /* LF */ && ch !== 0x0d /* CR */ && ch !== 0); + } + + if (is_EOL(ch)) { + readLineBreak(state); + + ch = state.input.charCodeAt(state.position); + lineBreaks++; + state.lineIndent = 0; + + while (ch === 0x20 /* Space */) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + } else { + break; + } + } + + if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) { + throwWarning(state, 'deficient indentation'); + } + + return lineBreaks; + } + + function testDocumentSeparator(state) { + var _position = state.position, + ch; + + ch = state.input.charCodeAt(_position); + + // Condition state.position === state.lineStart is tested + // in parent on each call, for efficiency. No needs to test here again. + if ( + (ch === 0x2d /* - */ || ch === 0x2e) /* . */ && + ch === state.input.charCodeAt(_position + 1) && + ch === state.input.charCodeAt(_position + 2) + ) { + _position += 3; + + ch = state.input.charCodeAt(_position); + + if (ch === 0 || is_WS_OR_EOL(ch)) { + return true; + } + } + + return false; + } + + function writeFoldedLines(state, count) { + if (count === 1) { + state.result += ' '; + } else if (count > 1) { + state.result += common.repeat('\n', count - 1); + } + } + + function readPlainScalar(state, nodeIndent, withinFlowCollection) { + var preceding, + following, + captureStart, + captureEnd, + hasPendingContent, + _line, + _lineStart, + _lineIndent, + _kind = state.kind, + _result = state.result, + ch; + + ch = state.input.charCodeAt(state.position); + + if ( + is_WS_OR_EOL(ch) || + is_FLOW_INDICATOR(ch) || + ch === 0x23 /* # */ || + ch === 0x26 /* & */ || + ch === 0x2a /* * */ || + ch === 0x21 /* ! */ || + ch === 0x7c /* | */ || + ch === 0x3e /* > */ || + ch === 0x27 /* ' */ || + ch === 0x22 /* " */ || + ch === 0x25 /* % */ || + ch === 0x40 /* @ */ || + ch === 0x60 /* ` */ + ) { + return false; + } + + if (ch === 0x3f /* ? */ || ch === 0x2d /* - */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following) || (withinFlowCollection && is_FLOW_INDICATOR(following))) { + return false; + } + } + + state.kind = 'scalar'; + state.result = ''; + captureStart = captureEnd = state.position; + hasPendingContent = false; + + while (ch !== 0) { + if (ch === 0x3a /* : */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following) || (withinFlowCollection && is_FLOW_INDICATOR(following))) { + break; + } + } else if (ch === 0x23 /* # */) { + preceding = state.input.charCodeAt(state.position - 1); + + if (is_WS_OR_EOL(preceding)) { + break; + } + } else if ( + (state.position === state.lineStart && testDocumentSeparator(state)) || + (withinFlowCollection && is_FLOW_INDICATOR(ch)) + ) { + break; + } else if (is_EOL(ch)) { + _line = state.line; + _lineStart = state.lineStart; + _lineIndent = state.lineIndent; + skipSeparationSpace(state, false, -1); + + if (state.lineIndent >= nodeIndent) { + hasPendingContent = true; + ch = state.input.charCodeAt(state.position); + continue; + } else { + state.position = captureEnd; + state.line = _line; + state.lineStart = _lineStart; + state.lineIndent = _lineIndent; + break; + } + } + + if (hasPendingContent) { + captureSegment(state, captureStart, captureEnd, false); + writeFoldedLines(state, state.line - _line); + captureStart = captureEnd = state.position; + hasPendingContent = false; + } + + if (!is_WHITE_SPACE(ch)) { + captureEnd = state.position + 1; + } + + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, captureEnd, false); + + if (state.result) { + return true; + } + + state.kind = _kind; + state.result = _result; + return false; + } + + function readSingleQuotedScalar(state, nodeIndent) { + var ch, captureStart, captureEnd; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x27 /* ' */) { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + state.position++; + captureStart = captureEnd = state.position; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x27 /* ' */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x27 /* ' */) { + captureStart = state.position; + state.position++; + captureEnd = state.position; + } else { + return true; + } + } else if (is_EOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + } else if (state.position === state.lineStart && testDocumentSeparator(state)) { + throwError(state, 'unexpected end of the document within a single quoted scalar'); + } else { + state.position++; + captureEnd = state.position; + } + } + + throwError(state, 'unexpected end of the stream within a single quoted scalar'); + } + + function readDoubleQuotedScalar(state, nodeIndent) { + var captureStart, captureEnd, hexLength, hexResult, tmp, ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x22 /* " */) { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + state.position++; + captureStart = captureEnd = state.position; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x22 /* " */) { + captureSegment(state, captureStart, state.position, true); + state.position++; + return true; + } else if (ch === 0x5c /* \ */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (is_EOL(ch)) { + skipSeparationSpace(state, false, nodeIndent); + + // TODO: rework to inline fn with no type cast? + } else if (ch < 256 && simpleEscapeCheck[ch]) { + state.result += simpleEscapeMap[ch]; + state.position++; + } else if ((tmp = escapedHexLen(ch)) > 0) { + hexLength = tmp; + hexResult = 0; + + for (; hexLength > 0; hexLength--) { + ch = state.input.charCodeAt(++state.position); + + if ((tmp = fromHexCode(ch)) >= 0) { + hexResult = (hexResult << 4) + tmp; + } else { + throwError(state, 'expected hexadecimal character'); + } + } + + state.result += charFromCodepoint(hexResult); + + state.position++; + } else { + throwError(state, 'unknown escape sequence'); + } + + captureStart = captureEnd = state.position; + } else if (is_EOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + } else if (state.position === state.lineStart && testDocumentSeparator(state)) { + throwError(state, 'unexpected end of the document within a double quoted scalar'); + } else { + state.position++; + captureEnd = state.position; + } + } + + throwError(state, 'unexpected end of the stream within a double quoted scalar'); + } + + function readFlowCollection(state, nodeIndent) { + var readNext = true, + _line, + _lineStart, + _pos, + _tag = state.tag, + _result, + _anchor = state.anchor, + following, + terminator, + isPair, + isExplicitPair, + isMapping, + overridableKeys = Object.create(null), + keyNode, + keyTag, + valueNode, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x5b /* [ */) { + terminator = 0x5d; /* ] */ + isMapping = false; + _result = []; + } else if (ch === 0x7b /* { */) { + terminator = 0x7d; /* } */ + isMapping = true; + _result = {}; + } else { + return false; + } + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(++state.position); + + while (ch !== 0) { + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === terminator) { + state.position++; + state.tag = _tag; + state.anchor = _anchor; + state.kind = isMapping ? 'mapping' : 'sequence'; + state.result = _result; + return true; + } else if (!readNext) { + throwError(state, 'missed comma between flow collection entries'); + } else if (ch === 0x2c /* , */) { + // "flow collection entries can never be completely empty", as per YAML 1.2, section 7.4 + throwError(state, "expected the node content, but found ','"); + } + + keyTag = keyNode = valueNode = null; + isPair = isExplicitPair = false; + + if (ch === 0x3f /* ? */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following)) { + isPair = isExplicitPair = true; + state.position++; + skipSeparationSpace(state, true, nodeIndent); + } + } + + _line = state.line; // Save the current line. + _lineStart = state.lineStart; + _pos = state.position; + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + keyTag = state.tag; + keyNode = state.result; + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if ((isExplicitPair || state.line === _line) && ch === 0x3a /* : */) { + isPair = true; + ch = state.input.charCodeAt(++state.position); + skipSeparationSpace(state, true, nodeIndent); + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + valueNode = state.result; + } + + if (isMapping) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos); + } else if (isPair) { + _result.push( + storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos) + ); + } else { + _result.push(keyNode); + } + + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x2c /* , */) { + readNext = true; + ch = state.input.charCodeAt(++state.position); + } else { + readNext = false; + } + } + + throwError(state, 'unexpected end of the stream within a flow collection'); + } + + function readBlockScalar(state, nodeIndent) { + var captureStart, + folding, + chomping = CHOMPING_CLIP, + didReadContent = false, + detectedIndent = false, + textIndent = nodeIndent, + emptyLines = 0, + atMoreIndented = false, + tmp, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x7c /* | */) { + folding = false; + } else if (ch === 0x3e /* > */) { + folding = true; + } else { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + + while (ch !== 0) { + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x2b /* + */ || ch === 0x2d /* - */) { + if (CHOMPING_CLIP === chomping) { + chomping = ch === 0x2b /* + */ ? CHOMPING_KEEP : CHOMPING_STRIP; + } else { + throwError(state, 'repeat of a chomping mode identifier'); + } + } else if ((tmp = fromDecimalCode(ch)) >= 0) { + if (tmp === 0) { + throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one'); + } else if (!detectedIndent) { + textIndent = nodeIndent + tmp - 1; + detectedIndent = true; + } else { + throwError(state, 'repeat of an indentation width identifier'); + } + } else { + break; + } + } + + if (is_WHITE_SPACE(ch)) { + do { + ch = state.input.charCodeAt(++state.position); + } while (is_WHITE_SPACE(ch)); + + if (ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (!is_EOL(ch) && ch !== 0); + } + } + + while (ch !== 0) { + readLineBreak(state); + state.lineIndent = 0; + + ch = state.input.charCodeAt(state.position); + + while ((!detectedIndent || state.lineIndent < textIndent) && ch === 0x20 /* Space */) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + + if (!detectedIndent && state.lineIndent > textIndent) { + textIndent = state.lineIndent; + } + + if (is_EOL(ch)) { + emptyLines++; + continue; + } + + // End of the scalar. + if (state.lineIndent < textIndent) { + // Perform the chomping. + if (chomping === CHOMPING_KEEP) { + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + } else if (chomping === CHOMPING_CLIP) { + if (didReadContent) { + // i.e. only if the scalar is not empty. + state.result += '\n'; + } + } + + // Break this `while` cycle and go to the funciton's epilogue. + break; + } + + // Folded style: use fancy rules to handle line breaks. + if (folding) { + // Lines starting with white space characters (more-indented lines) are not folded. + if (is_WHITE_SPACE(ch)) { + atMoreIndented = true; + // except for the first content line (cf. Example 8.1) + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + + // End of more-indented block. + } else if (atMoreIndented) { + atMoreIndented = false; + state.result += common.repeat('\n', emptyLines + 1); + + // Just one line break - perceive as the same line. + } else if (emptyLines === 0) { + if (didReadContent) { + // i.e. only if we have already read some scalar content. + state.result += ' '; + } + + // Several line breaks - perceive as different lines. + } else { + state.result += common.repeat('\n', emptyLines); + } + + // Literal style: just add exact number of line breaks between content lines. + } else { + // Keep all line breaks except the header line break. + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + } + + didReadContent = true; + detectedIndent = true; + emptyLines = 0; + captureStart = state.position; + + while (!is_EOL(ch) && ch !== 0) { + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, state.position, false); + } + + return true; + } + + function readBlockSequence(state, nodeIndent) { + var _line, + _tag = state.tag, + _anchor = state.anchor, + _result = [], + following, + detected = false, + ch; + + // there is a leading tab before this token, so it can't be a block sequence/mapping; + // it can still be flow sequence/mapping or a scalar + if (state.firstTabInLine !== -1) return false; + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + if (state.firstTabInLine !== -1) { + state.position = state.firstTabInLine; + throwError(state, 'tab characters must not be used in indentation'); + } + + if (ch !== 0x2d /* - */) { + break; + } + + following = state.input.charCodeAt(state.position + 1); + + if (!is_WS_OR_EOL(following)) { + break; + } + + detected = true; + state.position++; + + if (skipSeparationSpace(state, true, -1)) { + if (state.lineIndent <= nodeIndent) { + _result.push(null); + ch = state.input.charCodeAt(state.position); + continue; + } + } + + _line = state.line; + composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); + _result.push(state.result); + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if ((state.line === _line || state.lineIndent > nodeIndent) && ch !== 0) { + throwError(state, 'bad indentation of a sequence entry'); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + if (detected) { + state.tag = _tag; + state.anchor = _anchor; + state.kind = 'sequence'; + state.result = _result; + return true; + } + return false; + } + + function readBlockMapping(state, nodeIndent, flowIndent) { + var following, + allowCompact, + _line, + _keyLine, + _keyLineStart, + _keyPos, + _tag = state.tag, + _anchor = state.anchor, + _result = {}, + overridableKeys = Object.create(null), + keyTag = null, + keyNode = null, + valueNode = null, + atExplicitKey = false, + detected = false, + ch; + + // there is a leading tab before this token, so it can't be a block sequence/mapping; + // it can still be flow sequence/mapping or a scalar + if (state.firstTabInLine !== -1) return false; + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + if (!atExplicitKey && state.firstTabInLine !== -1) { + state.position = state.firstTabInLine; + throwError(state, 'tab characters must not be used in indentation'); + } + + following = state.input.charCodeAt(state.position + 1); + _line = state.line; // Save the current line. + + // + // Explicit notation case. There are two separate blocks: + // first for the key (denoted by "?") and second for the value (denoted by ":") + // + if ((ch === 0x3f /* ? */ || ch === 0x3a /*: */) && is_WS_OR_EOL(following)) { + if (ch === 0x3f /* ? */) { + if (atExplicitKey) { + storeMappingPair( + state, + _result, + overridableKeys, + keyTag, + keyNode, + null, + _keyLine, + _keyLineStart, + _keyPos + ); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = true; + allowCompact = true; + } else if (atExplicitKey) { + // i.e. 0x3A/* : */ === character after the explicit key. + atExplicitKey = false; + allowCompact = true; + } else { + throwError( + state, + 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line' + ); + } + + state.position += 1; + ch = following; + + // + // Implicit notation case. Flow-style node as the key first, then ":", and the value. + // + } else { + _keyLine = state.line; + _keyLineStart = state.lineStart; + _keyPos = state.position; + + if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { + // Neither implicit nor explicit notation. + // Reading is done. Go to the epilogue. + break; + } + + if (state.line === _line) { + ch = state.input.charCodeAt(state.position); + + while (is_WHITE_SPACE(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x3a /* : */) { + ch = state.input.charCodeAt(++state.position); + + if (!is_WS_OR_EOL(ch)) { + throwError( + state, + 'a whitespace character is expected after the key-value separator within a block mapping' + ); + } + + if (atExplicitKey) { + storeMappingPair( + state, + _result, + overridableKeys, + keyTag, + keyNode, + null, + _keyLine, + _keyLineStart, + _keyPos + ); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = false; + allowCompact = false; + keyTag = state.tag; + keyNode = state.result; + } else if (detected) { + throwError(state, 'can not read an implicit mapping pair; a colon is missed'); + } else { + state.tag = _tag; + state.anchor = _anchor; + return true; // Keep the result of `composeNode`. + } + } else if (detected) { + throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key'); + } else { + state.tag = _tag; + state.anchor = _anchor; + return true; // Keep the result of `composeNode`. + } + } + + // + // Common reading code for both explicit and implicit notations. + // + if (state.line === _line || state.lineIndent > nodeIndent) { + if (atExplicitKey) { + _keyLine = state.line; + _keyLineStart = state.lineStart; + _keyPos = state.position; + } + + if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) { + if (atExplicitKey) { + keyNode = state.result; + } else { + valueNode = state.result; + } + } + + if (!atExplicitKey) { + storeMappingPair( + state, + _result, + overridableKeys, + keyTag, + keyNode, + valueNode, + _keyLine, + _keyLineStart, + _keyPos + ); + keyTag = keyNode = valueNode = null; + } + + skipSeparationSpace(state, true, -1); + ch = state.input.charCodeAt(state.position); + } + + if ((state.line === _line || state.lineIndent > nodeIndent) && ch !== 0) { + throwError(state, 'bad indentation of a mapping entry'); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + // + // Epilogue. + // + + // Special case: last mapping's node contains only the key in explicit notation. + if (atExplicitKey) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos); + } + + // Expose the resulting mapping. + if (detected) { + state.tag = _tag; + state.anchor = _anchor; + state.kind = 'mapping'; + state.result = _result; + } + + return detected; + } + + function readTagProperty(state) { + var _position, + isVerbatim = false, + isNamed = false, + tagHandle, + tagName, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x21 /* ! */) return false; + + if (state.tag !== null) { + throwError(state, 'duplication of a tag property'); + } + + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x3c /* < */) { + isVerbatim = true; + ch = state.input.charCodeAt(++state.position); + } else if (ch === 0x21 /* ! */) { + isNamed = true; + tagHandle = '!!'; + ch = state.input.charCodeAt(++state.position); + } else { + tagHandle = '!'; + } + + _position = state.position; + + if (isVerbatim) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0 && ch !== 0x3e /* > */); + + if (state.position < state.length) { + tagName = state.input.slice(_position, state.position); + ch = state.input.charCodeAt(++state.position); + } else { + throwError(state, 'unexpected end of the stream within a verbatim tag'); + } + } else { + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + if (ch === 0x21 /* ! */) { + if (!isNamed) { + tagHandle = state.input.slice(_position - 1, state.position + 1); + + if (!PATTERN_TAG_HANDLE.test(tagHandle)) { + throwError(state, 'named tag handle cannot contain such characters'); + } + + isNamed = true; + _position = state.position + 1; + } else { + throwError(state, 'tag suffix cannot contain exclamation marks'); + } + } + + ch = state.input.charCodeAt(++state.position); + } + + tagName = state.input.slice(_position, state.position); + + if (PATTERN_FLOW_INDICATORS.test(tagName)) { + throwError(state, 'tag suffix cannot contain flow indicator characters'); + } + } + + if (tagName && !PATTERN_TAG_URI.test(tagName)) { + throwError(state, 'tag name cannot contain such characters: ' + tagName); + } + + try { + tagName = decodeURIComponent(tagName); + } catch (err) { + throwError(state, 'tag name is malformed: ' + tagName); + } + + if (isVerbatim) { + state.tag = tagName; + } else if (_hasOwnProperty$1.call(state.tagMap, tagHandle)) { + state.tag = state.tagMap[tagHandle] + tagName; + } else if (tagHandle === '!') { + state.tag = '!' + tagName; + } else if (tagHandle === '!!') { + state.tag = 'tag:yaml.org,2002:' + tagName; + } else { + throwError(state, 'undeclared tag handle "' + tagHandle + '"'); + } + + return true; + } + + function readAnchorProperty(state) { + var _position, ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x26 /* & */) return false; + + if (state.anchor !== null) { + throwError(state, 'duplication of an anchor property'); + } + + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === _position) { + throwError(state, 'name of an anchor node must contain at least one character'); + } + + state.anchor = state.input.slice(_position, state.position); + return true; + } + + function readAlias(state) { + var _position, alias, ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x2a /* * */) return false; + + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === _position) { + throwError(state, 'name of an alias node must contain at least one character'); + } + + alias = state.input.slice(_position, state.position); + + if (!_hasOwnProperty$1.call(state.anchorMap, alias)) { + throwError(state, 'unidentified alias "' + alias + '"'); + } + + state.result = state.anchorMap[alias]; + skipSeparationSpace(state, true, -1); + return true; + } + + function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) { + var allowBlockStyles, + allowBlockScalars, + allowBlockCollections, + indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } + } + + if (indentStatus === 1) { + while (readTagProperty(state) || readAnchorProperty(state)) { + if (skipSeparationSpace(state, true, -1)) { + atNewLine = true; + allowBlockCollections = allowBlockStyles; + + if (state.lineIndent > parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } else { + allowBlockCollections = false; + } + } + } + + if (allowBlockCollections) { + allowBlockCollections = atNewLine || allowCompact; + } + + if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { + if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) { + flowIndent = parentIndent; + } else { + flowIndent = parentIndent + 1; + } + + blockIndent = state.position - state.lineStart; + + if (indentStatus === 1) { + if ( + (allowBlockCollections && + (readBlockSequence(state, blockIndent) || readBlockMapping(state, blockIndent, flowIndent))) || + readFlowCollection(state, flowIndent) + ) { + hasContent = true; + } else { + if ( + (allowBlockScalars && readBlockScalar(state, flowIndent)) || + readSingleQuotedScalar(state, flowIndent) || + readDoubleQuotedScalar(state, flowIndent) + ) { + hasContent = true; + } else if (readAlias(state)) { + hasContent = true; + + if (state.tag !== null || state.anchor !== null) { + throwError(state, 'alias node should not have any properties'); + } + } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) { + hasContent = true; + + if (state.tag === null) { + state.tag = '?'; + } + } + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + } + } else if (indentStatus === 0) { + // Special case: block sequences are allowed to have same indentation level as the parent. + // http://www.yaml.org/spec/1.2/spec.html#id2799784 + hasContent = allowBlockCollections && readBlockSequence(state, blockIndent); + } + } + + if (state.tag === null) { + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + } else if (state.tag === '?') { + // Implicit resolving is not allowed for non-scalar types, and '?' + // non-specific tag is only automatically assigned to plain scalars. + // + // We only need to check kind conformity in case user explicitly assigns '?' + // tag, for example like this: "! [0]" + // + if (state.result !== null && state.kind !== 'scalar') { + throwError( + state, + 'unacceptable node kind for ! tag; it should be "scalar", not "' + state.kind + '"' + ); + } + + for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) { + type = state.implicitTypes[typeIndex]; + + if (type.resolve(state.result)) { + // `state.result` updated in resolver if matched + state.result = type.construct(state.result); + state.tag = type.tag; + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + break; + } + } + } else if (state.tag !== '!') { + if (_hasOwnProperty$1.call(state.typeMap[state.kind || 'fallback'], state.tag)) { + type = state.typeMap[state.kind || 'fallback'][state.tag]; + } else { + // looking for multi type + type = null; + typeList = state.typeMap.multi[state.kind || 'fallback']; + + for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) { + if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) { + type = typeList[typeIndex]; + break; + } + } + } + + if (!type) { + throwError(state, 'unknown tag !<' + state.tag + '>'); + } + + if (state.result !== null && type.kind !== state.kind) { + throwError( + state, + 'unacceptable node kind for !<' + + state.tag + + '> tag; it should be "' + + type.kind + + '", not "' + + state.kind + + '"' + ); + } + + if (!type.resolve(state.result, state.tag)) { + // `state.result` updated in resolver if matched + throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag'); + } else { + state.result = type.construct(state.result, state.tag); + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + } + } + + if (state.listener !== null) { + state.listener('close', state); + } + return state.tag !== null || state.anchor !== null || hasContent; + } + + function readDocument(state) { + var documentStart = state.position, + _position, + directiveName, + directiveArgs, + hasDirectives = false, + ch; + + state.version = null; + state.checkLineBreaks = state.legacy; + state.tagMap = Object.create(null); + state.anchorMap = Object.create(null); + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if (state.lineIndent > 0 || ch !== 0x25 /* % */) { + break; + } + + hasDirectives = true; + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveName = state.input.slice(_position, state.position); + directiveArgs = []; + + if (directiveName.length < 1) { + throwError(state, 'directive name must not be less than one character in length'); + } + + while (ch !== 0) { + while (is_WHITE_SPACE(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0 && !is_EOL(ch)); + break; + } + + if (is_EOL(ch)) break; + + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveArgs.push(state.input.slice(_position, state.position)); + } + + if (ch !== 0) readLineBreak(state); + + if (_hasOwnProperty$1.call(directiveHandlers, directiveName)) { + directiveHandlers[directiveName](state, directiveName, directiveArgs); + } else { + throwWarning(state, 'unknown document directive "' + directiveName + '"'); + } + } + + skipSeparationSpace(state, true, -1); + + if ( + state.lineIndent === 0 && + state.input.charCodeAt(state.position) === 0x2d /* - */ && + state.input.charCodeAt(state.position + 1) === 0x2d /* - */ && + state.input.charCodeAt(state.position + 2) === 0x2d /* - */ + ) { + state.position += 3; + skipSeparationSpace(state, true, -1); + } else if (hasDirectives) { + throwError(state, 'directives end mark is expected'); + } + + composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); + skipSeparationSpace(state, true, -1); + + if ( + state.checkLineBreaks && + PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position)) + ) { + throwWarning(state, 'non-ASCII line breaks are interpreted as content'); + } + + state.documents.push(state.result); + + if (state.position === state.lineStart && testDocumentSeparator(state)) { + if (state.input.charCodeAt(state.position) === 0x2e /* . */) { + state.position += 3; + skipSeparationSpace(state, true, -1); + } + return; + } + + if (state.position < state.length - 1) { + throwError(state, 'end of the stream or a document separator is expected'); + } else { + return; + } + } + + function loadDocuments(input, options) { + input = String(input); + options = options || {}; + + if (input.length !== 0) { + // Add tailing `\n` if not exists + if ( + input.charCodeAt(input.length - 1) !== 0x0a /* LF */ && + input.charCodeAt(input.length - 1) !== 0x0d /* CR */ + ) { + input += '\n'; + } + + // Strip BOM + if (input.charCodeAt(0) === 0xfeff) { + input = input.slice(1); + } + } + + var state = new State$1(input, options); + + var nullpos = input.indexOf('\0'); + + if (nullpos !== -1) { + state.position = nullpos; + throwError(state, 'null byte is not allowed in input'); + } + + // Use 0 as string terminator. That significantly simplifies bounds check. + state.input += '\0'; + + while (state.input.charCodeAt(state.position) === 0x20 /* Space */) { + state.lineIndent += 1; + state.position += 1; + } + + while (state.position < state.length - 1) { + readDocument(state); + } + + return state.documents; + } + + function loadAll$1(input, iterator, options) { + if (iterator !== null && typeof iterator === 'object' && typeof options === 'undefined') { + options = iterator; + iterator = null; + } + + var documents = loadDocuments(input, options); + + if (typeof iterator !== 'function') { + return documents; + } + + for (var index = 0, length = documents.length; index < length; index += 1) { + iterator(documents[index]); + } + } + + function load$1(input, options) { + var documents = loadDocuments(input, options); + + if (documents.length === 0) { + /*eslint-disable no-undefined*/ + return undefined; + } else if (documents.length === 1) { + return documents[0]; + } + throw new exception('expected a single document in the stream, but found more'); + } + + var loadAll_1 = loadAll$1; + var load_1 = load$1; + + var loader = { + loadAll: loadAll_1, + load: load_1, + }; + + /*eslint-disable no-use-before-define*/ + + var _toString = Object.prototype.toString; + var _hasOwnProperty = Object.prototype.hasOwnProperty; + + var CHAR_BOM = 0xfeff; + var CHAR_TAB = 0x09; /* Tab */ + var CHAR_LINE_FEED = 0x0a; /* LF */ + var CHAR_CARRIAGE_RETURN = 0x0d; /* CR */ + var CHAR_SPACE = 0x20; /* Space */ + var CHAR_EXCLAMATION = 0x21; /* ! */ + var CHAR_DOUBLE_QUOTE = 0x22; /* " */ + var CHAR_SHARP = 0x23; /* # */ + var CHAR_PERCENT = 0x25; /* % */ + var CHAR_AMPERSAND = 0x26; /* & */ + var CHAR_SINGLE_QUOTE = 0x27; /* ' */ + var CHAR_ASTERISK = 0x2a; /* * */ + var CHAR_COMMA = 0x2c; /* , */ + var CHAR_MINUS = 0x2d; /* - */ + var CHAR_COLON = 0x3a; /* : */ + var CHAR_EQUALS = 0x3d; /* = */ + var CHAR_GREATER_THAN = 0x3e; /* > */ + var CHAR_QUESTION = 0x3f; /* ? */ + var CHAR_COMMERCIAL_AT = 0x40; /* @ */ + var CHAR_LEFT_SQUARE_BRACKET = 0x5b; /* [ */ + var CHAR_RIGHT_SQUARE_BRACKET = 0x5d; /* ] */ + var CHAR_GRAVE_ACCENT = 0x60; /* ` */ + var CHAR_LEFT_CURLY_BRACKET = 0x7b; /* { */ + var CHAR_VERTICAL_LINE = 0x7c; /* | */ + var CHAR_RIGHT_CURLY_BRACKET = 0x7d; /* } */ + + var ESCAPE_SEQUENCES = {}; + + ESCAPE_SEQUENCES[0x00] = '\\0'; + ESCAPE_SEQUENCES[0x07] = '\\a'; + ESCAPE_SEQUENCES[0x08] = '\\b'; + ESCAPE_SEQUENCES[0x09] = '\\t'; + ESCAPE_SEQUENCES[0x0a] = '\\n'; + ESCAPE_SEQUENCES[0x0b] = '\\v'; + ESCAPE_SEQUENCES[0x0c] = '\\f'; + ESCAPE_SEQUENCES[0x0d] = '\\r'; + ESCAPE_SEQUENCES[0x1b] = '\\e'; + ESCAPE_SEQUENCES[0x22] = '\\"'; + ESCAPE_SEQUENCES[0x5c] = '\\\\'; + ESCAPE_SEQUENCES[0x85] = '\\N'; + ESCAPE_SEQUENCES[0xa0] = '\\_'; + ESCAPE_SEQUENCES[0x2028] = '\\L'; + ESCAPE_SEQUENCES[0x2029] = '\\P'; + + var DEPRECATED_BOOLEANS_SYNTAX = [ + 'y', + 'Y', + 'yes', + 'Yes', + 'YES', + 'on', + 'On', + 'ON', + 'n', + 'N', + 'no', + 'No', + 'NO', + 'off', + 'Off', + 'OFF', + ]; + + var DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/; + + function compileStyleMap(schema, map) { + var result, keys, index, length, tag, style, type; + + if (map === null) return {}; + + result = {}; + keys = Object.keys(map); + + for (index = 0, length = keys.length; index < length; index += 1) { + tag = keys[index]; + style = String(map[tag]); + + if (tag.slice(0, 2) === '!!') { + tag = 'tag:yaml.org,2002:' + tag.slice(2); + } + type = schema.compiledTypeMap['fallback'][tag]; + + if (type && _hasOwnProperty.call(type.styleAliases, style)) { + style = type.styleAliases[style]; + } + + result[tag] = style; + } + + return result; + } + + function encodeHex(character) { + var string, handle, length; + + string = character.toString(16).toUpperCase(); + + if (character <= 0xff) { + handle = 'x'; + length = 2; + } else if (character <= 0xffff) { + handle = 'u'; + length = 4; + } else if (character <= 0xffffffff) { + handle = 'U'; + length = 8; + } else { + throw new exception('code point within a string may not be greater than 0xFFFFFFFF'); + } + + return '\\' + handle + common.repeat('0', length - string.length) + string; + } + + var QUOTING_TYPE_SINGLE = 1, + QUOTING_TYPE_DOUBLE = 2; + + function State(options) { + this.schema = options['schema'] || _default; + this.indent = Math.max(1, options['indent'] || 2); + this.noArrayIndent = options['noArrayIndent'] || false; + this.skipInvalid = options['skipInvalid'] || false; + this.flowLevel = common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']; + this.styleMap = compileStyleMap(this.schema, options['styles'] || null); + this.sortKeys = options['sortKeys'] || false; + this.lineWidth = options['lineWidth'] || 80; + this.noRefs = options['noRefs'] || false; + this.noCompatMode = options['noCompatMode'] || false; + this.condenseFlow = options['condenseFlow'] || false; + this.quotingType = options['quotingType'] === '"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE; + this.forceQuotes = options['forceQuotes'] || false; + this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null; + + this.implicitTypes = this.schema.compiledImplicit; + this.explicitTypes = this.schema.compiledExplicit; + + this.tag = null; + this.result = ''; + + this.duplicates = []; + this.usedDuplicates = null; + } + + // Indents every line in a string. Empty lines (\n only) are not indented. + function indentString(string, spaces) { + var ind = common.repeat(' ', spaces), + position = 0, + next = -1, + result = '', + line, + length = string.length; + + while (position < length) { + next = string.indexOf('\n', position); + if (next === -1) { + line = string.slice(position); + position = length; + } else { + line = string.slice(position, next + 1); + position = next + 1; + } + + if (line.length && line !== '\n') result += ind; + + result += line; + } + + return result; + } + + function generateNextLine(state, level) { + return '\n' + common.repeat(' ', state.indent * level); + } + + function testImplicitResolving(state, str) { + var index, length, type; + + for (index = 0, length = state.implicitTypes.length; index < length; index += 1) { + type = state.implicitTypes[index]; + + if (type.resolve(str)) { + return true; + } + } + + return false; + } + + // [33] s-white ::= s-space | s-tab + function isWhitespace(c) { + return c === CHAR_SPACE || c === CHAR_TAB; + } + + // Returns true if the character can be printed without escaping. + // From YAML 1.2: "any allowed characters known to be non-printable + // should also be escaped. [However,] This isn’t mandatory" + // Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029. + function isPrintable(c) { + return ( + (0x00020 <= c && c <= 0x00007e) || + (0x000a1 <= c && c <= 0x00d7ff && c !== 0x2028 && c !== 0x2029) || + (0x0e000 <= c && c <= 0x00fffd && c !== CHAR_BOM) || + (0x10000 <= c && c <= 0x10ffff) + ); + } + + // [34] ns-char ::= nb-char - s-white + // [27] nb-char ::= c-printable - b-char - c-byte-order-mark + // [26] b-char ::= b-line-feed | b-carriage-return + // Including s-white (for some reason, examples doesn't match specs in this aspect) + // ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark + function isNsCharOrWhitespace(c) { + return ( + isPrintable(c) && + c !== CHAR_BOM && + // - b-char + c !== CHAR_CARRIAGE_RETURN && + c !== CHAR_LINE_FEED + ); + } + + // [127] ns-plain-safe(c) ::= c = flow-out ⇒ ns-plain-safe-out + // c = flow-in ⇒ ns-plain-safe-in + // c = block-key ⇒ ns-plain-safe-out + // c = flow-key ⇒ ns-plain-safe-in + // [128] ns-plain-safe-out ::= ns-char + // [129] ns-plain-safe-in ::= ns-char - c-flow-indicator + // [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - “:” - “#” ) + // | ( /* An ns-char preceding */ “#” ) + // | ( “:” /* Followed by an ns-plain-safe(c) */ ) + function isPlainSafe(c, prev, inblock) { + var cIsNsCharOrWhitespace = isNsCharOrWhitespace(c); + var cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c); + return ( + (// ns-plain-safe + (inblock // c = flow-in + ? cIsNsCharOrWhitespace + : cIsNsCharOrWhitespace && + // - c-flow-indicator + c !== CHAR_COMMA && + c !== CHAR_LEFT_SQUARE_BRACKET && + c !== CHAR_RIGHT_SQUARE_BRACKET && + c !== CHAR_LEFT_CURLY_BRACKET && + c !== CHAR_RIGHT_CURLY_BRACKET) && + // ns-plain-char + c !== CHAR_SHARP && // false on '#' + !(prev === CHAR_COLON && !cIsNsChar)) || // false on ': ' + (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) || // change to true on '[^ ]#' + (prev === CHAR_COLON && cIsNsChar) + ); // change to true on ':[^ ]' + } + + // Simplified test for values allowed as the first character in plain style. + function isPlainSafeFirst(c) { + // Uses a subset of ns-char - c-indicator + // where ns-char = nb-char - s-white. + // No support of ( ( “?” | “:” | “-” ) /* Followed by an ns-plain-safe(c)) */ ) part + return ( + isPrintable(c) && + c !== CHAR_BOM && + !isWhitespace(c) && // - s-white + // - (c-indicator ::= + // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}” + c !== CHAR_MINUS && + c !== CHAR_QUESTION && + c !== CHAR_COLON && + c !== CHAR_COMMA && + c !== CHAR_LEFT_SQUARE_BRACKET && + c !== CHAR_RIGHT_SQUARE_BRACKET && + c !== CHAR_LEFT_CURLY_BRACKET && + c !== CHAR_RIGHT_CURLY_BRACKET && + // | “#” | “&” | “*” | “!” | “|” | “=” | “>” | “'” | “"” + c !== CHAR_SHARP && + c !== CHAR_AMPERSAND && + c !== CHAR_ASTERISK && + c !== CHAR_EXCLAMATION && + c !== CHAR_VERTICAL_LINE && + c !== CHAR_EQUALS && + c !== CHAR_GREATER_THAN && + c !== CHAR_SINGLE_QUOTE && + c !== CHAR_DOUBLE_QUOTE && + // | “%” | “@” | “`”) + c !== CHAR_PERCENT && + c !== CHAR_COMMERCIAL_AT && + c !== CHAR_GRAVE_ACCENT + ); + } + + // Simplified test for values allowed as the last character in plain style. + function isPlainSafeLast(c) { + // just not whitespace or colon, it will be checked to be plain character later + return !isWhitespace(c) && c !== CHAR_COLON; + } + + // Same as 'string'.codePointAt(pos), but works in older browsers. + function codePointAt(string, pos) { + var first = string.charCodeAt(pos), + second; + if (first >= 0xd800 && first <= 0xdbff && pos + 1 < string.length) { + second = string.charCodeAt(pos + 1); + if (second >= 0xdc00 && second <= 0xdfff) { + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + return (first - 0xd800) * 0x400 + second - 0xdc00 + 0x10000; + } + } + return first; + } + + // Determines whether block indentation indicator is required. + function needIndentIndicator(string) { + var leadingSpaceRe = /^\n* /; + return leadingSpaceRe.test(string); + } + + var STYLE_PLAIN = 1, + STYLE_SINGLE = 2, + STYLE_LITERAL = 3, + STYLE_FOLDED = 4, + STYLE_DOUBLE = 5; + + // Determines which scalar styles are possible and returns the preferred style. + // lineWidth = -1 => no limit. + // Pre-conditions: str.length > 0. + // Post-conditions: + // STYLE_PLAIN or STYLE_SINGLE => no \n are in the string. + // STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1). + // STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1). + function chooseScalarStyle( + string, + singleLineOnly, + indentPerLevel, + lineWidth, + testAmbiguousType, + quotingType, + forceQuotes, + inblock + ) { + var i; + var char = 0; + var prevChar = null; + var hasLineBreak = false; + var hasFoldableLine = false; // only checked if shouldTrackWidth + var shouldTrackWidth = lineWidth !== -1; + var previousLineBreak = -1; // count the first line correctly + var plain = isPlainSafeFirst(codePointAt(string, 0)) && isPlainSafeLast(codePointAt(string, string.length - 1)); + + if (singleLineOnly || forceQuotes) { + // Case: no block styles. + // Check for disallowed characters to rule out plain and single. + for (i = 0; i < string.length; char >= 0x10000 ? (i += 2) : i++) { + char = codePointAt(string, i); + if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char, prevChar, inblock); + prevChar = char; + } + } else { + // Case: block styles permitted. + for (i = 0; i < string.length; char >= 0x10000 ? (i += 2) : i++) { + char = codePointAt(string, i); + if (char === CHAR_LINE_FEED) { + hasLineBreak = true; + // Check if any line can be folded. + if (shouldTrackWidth) { + hasFoldableLine = + hasFoldableLine || + // Foldable line = too long, and not more-indented. + (i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== ' '); + previousLineBreak = i; + } + } else if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char, prevChar, inblock); + prevChar = char; + } + // in case the end is missing a \n + hasFoldableLine = + hasFoldableLine || + (shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== ' '); + } + // Although every style can represent \n without escaping, prefer block styles + // for multiline, since they're more readable and they don't add empty lines. + // Also prefer folding a super-long line. + if (!hasLineBreak && !hasFoldableLine) { + // Strings interpretable as another type have to be quoted; + // e.g. the string 'true' vs. the boolean true. + if (plain && !forceQuotes && !testAmbiguousType(string)) { + return STYLE_PLAIN; + } + return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE; + } + // Edge case: block indentation indicator can only have one digit. + if (indentPerLevel > 9 && needIndentIndicator(string)) { + return STYLE_DOUBLE; + } + // At this point we know block styles are valid. + // Prefer literal style unless we want to fold. + if (!forceQuotes) { + return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL; + } + return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE; + } + + // Note: line breaking/folding is implemented for only the folded style. + // NB. We drop the last trailing newline (if any) of a returned block scalar + // since the dumper adds its own newline. This always works: + // • No ending newline => unaffected; already using strip "-" chomping. + // • Ending newline => removed then restored. + // Importantly, this keeps the "+" chomp indicator from gaining an extra line. + function writeScalar(state, string, level, iskey, inblock) { + state.dump = (function () { + if (string.length === 0) { + return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''"; + } + if (!state.noCompatMode) { + if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) { + return state.quotingType === QUOTING_TYPE_DOUBLE ? '"' + string + '"' : "'" + string + "'"; + } + } + + var indent = state.indent * Math.max(1, level); // no 0-indent scalars + // As indentation gets deeper, let the width decrease monotonically + // to the lower bound min(state.lineWidth, 40). + // Note that this implies + // state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound. + // state.lineWidth > 40 + state.indent: width decreases until the lower bound. + // This behaves better than a constant minimum width which disallows narrower options, + // or an indent threshold which causes the width to suddenly increase. + var lineWidth = + state.lineWidth === -1 ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent); + + // Without knowing if keys are implicit/explicit, assume implicit for safety. + var singleLineOnly = + iskey || + // No block styles in flow mode. + (state.flowLevel > -1 && level >= state.flowLevel); + function testAmbiguity(string) { + return testImplicitResolving(state, string); + } + + switch ( + chooseScalarStyle( + string, + singleLineOnly, + state.indent, + lineWidth, + testAmbiguity, + state.quotingType, + state.forceQuotes && !iskey, + inblock + ) + ) { + case STYLE_PLAIN: + return string; + case STYLE_SINGLE: + return "'" + string.replace(/'/g, "''") + "'"; + case STYLE_LITERAL: + return '|' + blockHeader(string, state.indent) + dropEndingNewline(indentString(string, indent)); + case STYLE_FOLDED: + return ( + '>' + + blockHeader(string, state.indent) + + dropEndingNewline(indentString(foldString(string, lineWidth), indent)) + ); + case STYLE_DOUBLE: + return '"' + escapeString(string) + '"'; + default: + throw new exception('impossible error: invalid scalar style'); + } + })(); + } + + // Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9. + function blockHeader(string, indentPerLevel) { + var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''; + + // note the special case: the string '\n' counts as a "trailing" empty line. + var clip = string[string.length - 1] === '\n'; + var keep = clip && (string[string.length - 2] === '\n' || string === '\n'); + var chomp = keep ? '+' : clip ? '' : '-'; + + return indentIndicator + chomp + '\n'; + } + + // (See the note for writeScalar.) + function dropEndingNewline(string) { + return string[string.length - 1] === '\n' ? string.slice(0, -1) : string; + } + + // Note: a long line without a suitable break point will exceed the width limit. + // Pre-conditions: every char in str isPrintable, str.length > 0, width > 0. + function foldString(string, width) { + // In folded style, $k$ consecutive newlines output as $k+1$ newlines— + // unless they're before or after a more-indented line, or at the very + // beginning or end, in which case $k$ maps to $k$. + // Therefore, parse each chunk as newline(s) followed by a content line. + var lineRe = /(\n+)([^\n]*)/g; + + // first line (possibly an empty line) + var result = (function () { + var nextLF = string.indexOf('\n'); + nextLF = nextLF !== -1 ? nextLF : string.length; + lineRe.lastIndex = nextLF; + return foldLine(string.slice(0, nextLF), width); + })(); + // If we haven't reached the first content line yet, don't add an extra \n. + var prevMoreIndented = string[0] === '\n' || string[0] === ' '; + var moreIndented; + + // rest of the lines + var match; + while ((match = lineRe.exec(string))) { + var prefix = match[1], + line = match[2]; + moreIndented = line[0] === ' '; + result += prefix + (!prevMoreIndented && !moreIndented && line !== '' ? '\n' : '') + foldLine(line, width); + prevMoreIndented = moreIndented; + } + + return result; + } + + // Greedy line breaking. + // Picks the longest line under the limit each time, + // otherwise settles for the shortest line over the limit. + // NB. More-indented lines *cannot* be folded, as that would add an extra \n. + function foldLine(line, width) { + if (line === '' || line[0] === ' ') return line; + + // Since a more-indented line adds a \n, breaks can't be followed by a space. + var breakRe = / [^ ]/g; // note: the match index will always be <= length-2. + var match; + // start is an inclusive index. end, curr, and next are exclusive. + var start = 0, + end, + curr = 0, + next = 0; + var result = ''; + + // Invariants: 0 <= start <= length-1. + // 0 <= curr <= next <= max(0, length-2). curr - start <= width. + // Inside the loop: + // A match implies length >= 2, so curr and next are <= length-2. + while ((match = breakRe.exec(line))) { + next = match.index; + // maintain invariant: curr - start <= width + if (next - start > width) { + end = curr > start ? curr : next; // derive end <= length-2 + result += '\n' + line.slice(start, end); + // skip the space that was output as \n + start = end + 1; // derive start <= length-1 + } + curr = next; + } + + // By the invariants, start <= length-1, so there is something left over. + // It is either the whole string or a part starting from non-whitespace. + result += '\n'; + // Insert a break if the remainder is too long and there is a break available. + if (line.length - start > width && curr > start) { + result += line.slice(start, curr) + '\n' + line.slice(curr + 1); + } else { + result += line.slice(start); + } + + return result.slice(1); // drop extra \n joiner + } + + // Escapes a double-quoted string. + function escapeString(string) { + var result = ''; + var char = 0; + var escapeSeq; + + for (var i = 0; i < string.length; char >= 0x10000 ? (i += 2) : i++) { + char = codePointAt(string, i); + escapeSeq = ESCAPE_SEQUENCES[char]; + + if (!escapeSeq && isPrintable(char)) { + result += string[i]; + if (char >= 0x10000) result += string[i + 1]; + } else { + result += escapeSeq || encodeHex(char); + } + } + + return result; + } + + function writeFlowSequence(state, level, object) { + var _result = '', + _tag = state.tag, + index, + length, + value; + + for (index = 0, length = object.length; index < length; index += 1) { + value = object[index]; + + if (state.replacer) { + value = state.replacer.call(object, String(index), value); + } + + // Write only valid elements, put null instead of invalid elements. + if ( + writeNode(state, level, value, false, false) || + (typeof value === 'undefined' && writeNode(state, level, null, false, false)) + ) { + if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : ''); + _result += state.dump; + } + } + + state.tag = _tag; + state.dump = '[' + _result + ']'; + } + + function writeBlockSequence(state, level, object, compact) { + var _result = '', + _tag = state.tag, + index, + length, + value; + + for (index = 0, length = object.length; index < length; index += 1) { + value = object[index]; + + if (state.replacer) { + value = state.replacer.call(object, String(index), value); + } + + // Write only valid elements, put null instead of invalid elements. + if ( + writeNode(state, level + 1, value, true, true, false, true) || + (typeof value === 'undefined' && writeNode(state, level + 1, null, true, true, false, true)) + ) { + if (!compact || _result !== '') { + _result += generateNextLine(state, level); + } + + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + _result += '-'; + } else { + _result += '- '; + } + + _result += state.dump; + } + } + + state.tag = _tag; + state.dump = _result || '[]'; // Empty sequence if no valid values. + } + + function writeFlowMapping(state, level, object) { + var _result = '', + _tag = state.tag, + objectKeyList = Object.keys(object), + index, + length, + objectKey, + objectValue, + pairBuffer; + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + pairBuffer = ''; + if (_result !== '') pairBuffer += ', '; + + if (state.condenseFlow) pairBuffer += '"'; + + objectKey = objectKeyList[index]; + objectValue = object[objectKey]; + + if (state.replacer) { + objectValue = state.replacer.call(object, objectKey, objectValue); + } + + if (!writeNode(state, level, objectKey, false, false)) { + continue; // Skip this pair because of invalid key; + } + + if (state.dump.length > 1024) pairBuffer += '? '; + + pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' '); + + if (!writeNode(state, level, objectValue, false, false)) { + continue; // Skip this pair because of invalid value. + } + + pairBuffer += state.dump; + + // Both key and value are valid. + _result += pairBuffer; + } + + state.tag = _tag; + state.dump = '{' + _result + '}'; + } + + function writeBlockMapping(state, level, object, compact) { + var _result = '', + _tag = state.tag, + objectKeyList = Object.keys(object), + index, + length, + objectKey, + objectValue, + explicitPair, + pairBuffer; + + // Allow sorting keys so that the output file is deterministic + if (state.sortKeys === true) { + // Default sorting + objectKeyList.sort(); + } else if (typeof state.sortKeys === 'function') { + // Custom sort function + objectKeyList.sort(state.sortKeys); + } else if (state.sortKeys) { + // Something is wrong + throw new exception('sortKeys must be a boolean or a function'); + } + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + pairBuffer = ''; + + if (!compact || _result !== '') { + pairBuffer += generateNextLine(state, level); + } + + objectKey = objectKeyList[index]; + objectValue = object[objectKey]; + + if (state.replacer) { + objectValue = state.replacer.call(object, objectKey, objectValue); + } + + if (!writeNode(state, level + 1, objectKey, true, true, true)) { + continue; // Skip this pair because of invalid key. + } + + explicitPair = (state.tag !== null && state.tag !== '?') || (state.dump && state.dump.length > 1024); + + if (explicitPair) { + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + pairBuffer += '?'; + } else { + pairBuffer += '? '; + } + } + + pairBuffer += state.dump; + + if (explicitPair) { + pairBuffer += generateNextLine(state, level); + } + + if (!writeNode(state, level + 1, objectValue, true, explicitPair)) { + continue; // Skip this pair because of invalid value. + } + + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + pairBuffer += ':'; + } else { + pairBuffer += ': '; + } + + pairBuffer += state.dump; + + // Both key and value are valid. + _result += pairBuffer; + } + + state.tag = _tag; + state.dump = _result || '{}'; // Empty mapping if no valid pairs. + } + + function detectType(state, object, explicit) { + var _result, typeList, index, length, type, style; + + typeList = explicit ? state.explicitTypes : state.implicitTypes; + + for (index = 0, length = typeList.length; index < length; index += 1) { + type = typeList[index]; + + if ( + (type.instanceOf || type.predicate) && + (!type.instanceOf || (typeof object === 'object' && object instanceof type.instanceOf)) && + (!type.predicate || type.predicate(object)) + ) { + if (explicit) { + if (type.multi && type.representName) { + state.tag = type.representName(object); + } else { + state.tag = type.tag; + } + } else { + state.tag = '?'; + } + + if (type.represent) { + style = state.styleMap[type.tag] || type.defaultStyle; + + if (_toString.call(type.represent) === '[object Function]') { + _result = type.represent(object, style); + } else if (_hasOwnProperty.call(type.represent, style)) { + _result = type.represent[style](object, style); + } else { + throw new exception('!<' + type.tag + '> tag resolver accepts not "' + style + '" style'); + } + + state.dump = _result; + } + + return true; + } + } + + return false; + } + + // Serializes `object` and writes it to global `result`. + // Returns true on success, or false on invalid object. + // + function writeNode(state, level, object, block, compact, iskey, isblockseq) { + state.tag = null; + state.dump = object; + + if (!detectType(state, object, false)) { + detectType(state, object, true); + } + + var type = _toString.call(state.dump); + var inblock = block; + var tagStr; + + if (block) { + block = state.flowLevel < 0 || state.flowLevel > level; + } + + var objectOrArray = type === '[object Object]' || type === '[object Array]', + duplicateIndex, + duplicate; + + if (objectOrArray) { + duplicateIndex = state.duplicates.indexOf(object); + duplicate = duplicateIndex !== -1; + } + + if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) { + compact = false; + } + + if (duplicate && state.usedDuplicates[duplicateIndex]) { + state.dump = '*ref_' + duplicateIndex; + } else { + if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) { + state.usedDuplicates[duplicateIndex] = true; + } + if (type === '[object Object]') { + if (block && Object.keys(state.dump).length !== 0) { + writeBlockMapping(state, level, state.dump, compact); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + state.dump; + } + } else { + writeFlowMapping(state, level, state.dump); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; + } + } + } else if (type === '[object Array]') { + if (block && state.dump.length !== 0) { + if (state.noArrayIndent && !isblockseq && level > 0) { + writeBlockSequence(state, level - 1, state.dump, compact); + } else { + writeBlockSequence(state, level, state.dump, compact); + } + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + state.dump; + } + } else { + writeFlowSequence(state, level, state.dump); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; + } + } + } else if (type === '[object String]') { + if (state.tag !== '?') { + writeScalar(state, state.dump, level, iskey, inblock); + } + } else if (type === '[object Undefined]') { + return false; + } else { + if (state.skipInvalid) return false; + throw new exception('unacceptable kind of an object to dump ' + type); + } + + if (state.tag !== null && state.tag !== '?') { + // Need to encode all characters except those allowed by the spec: + // + // [35] ns-dec-digit ::= [#x30-#x39] /* 0-9 */ + // [36] ns-hex-digit ::= ns-dec-digit + // | [#x41-#x46] /* A-F */ | [#x61-#x66] /* a-f */ + // [37] ns-ascii-letter ::= [#x41-#x5A] /* A-Z */ | [#x61-#x7A] /* a-z */ + // [38] ns-word-char ::= ns-dec-digit | ns-ascii-letter | “-” + // [39] ns-uri-char ::= “%” ns-hex-digit ns-hex-digit | ns-word-char | “#” + // | “;” | “/” | “?” | “:” | “@” | “&” | “=” | “+” | “$” | “,” + // | “_” | “.” | “!” | “~” | “*” | “'” | “(” | “)” | “[” | “]” + // + // Also need to encode '!' because it has special meaning (end of tag prefix). + // + tagStr = encodeURI(state.tag[0] === '!' ? state.tag.slice(1) : state.tag).replace(/!/g, '%21'); + + if (state.tag[0] === '!') { + tagStr = '!' + tagStr; + } else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') { + tagStr = '!!' + tagStr.slice(18); + } else { + tagStr = '!<' + tagStr + '>'; + } + + state.dump = tagStr + ' ' + state.dump; + } + } + + return true; + } + + function getDuplicateReferences(object, state) { + var objects = [], + duplicatesIndexes = [], + index, + length; + + inspectNode(object, objects, duplicatesIndexes); + + for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) { + state.duplicates.push(objects[duplicatesIndexes[index]]); + } + state.usedDuplicates = new Array(length); + } + + function inspectNode(object, objects, duplicatesIndexes) { + var objectKeyList, index, length; + + if (object !== null && typeof object === 'object') { + index = objects.indexOf(object); + if (index !== -1) { + if (duplicatesIndexes.indexOf(index) === -1) { + duplicatesIndexes.push(index); + } + } else { + objects.push(object); + + if (Array.isArray(object)) { + for (index = 0, length = object.length; index < length; index += 1) { + inspectNode(object[index], objects, duplicatesIndexes); + } + } else { + objectKeyList = Object.keys(object); + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes); + } + } + } + } + } + + function dump$1(input, options) { + options = options || {}; + + var state = new State(options); + + if (!state.noRefs) getDuplicateReferences(input, state); + + var value = input; + + if (state.replacer) { + value = state.replacer.call({ '': value }, '', value); + } + + if (writeNode(state, 0, value, true, true)) return state.dump + '\n'; + + return ''; + } + + var dump_1 = dump$1; + + var dumper = { + dump: dump_1, + }; + + function renamed(from, to) { + return function () { + throw new Error( + 'Function yaml.' + + from + + ' is removed in js-yaml 4. ' + + 'Use yaml.' + + to + + ' instead, which is now safe by default.' + ); + }; + } + + var Type = type; + var Schema = schema; + var FAILSAFE_SCHEMA = failsafe; + var JSON_SCHEMA = json; + var CORE_SCHEMA = core; + var DEFAULT_SCHEMA = _default; + var load = loader.load; + var loadAll = loader.loadAll; + var dump = dumper.dump; + var YAMLException = exception; + + // Re-export all types in case user wants to create custom schema + var types = { + binary: binary, + float: float, + map: map, + null: _null, + pairs: pairs, + set: set, + timestamp: timestamp, + bool: bool, + int: int, + merge: merge, + omap: omap, + seq: seq, + str: str, + }; + + // Removed functions from JS-YAML 3.0.x + var safeLoad = renamed('safeLoad', 'load'); + var safeLoadAll = renamed('safeLoadAll', 'loadAll'); + var safeDump = renamed('safeDump', 'dump'); + + var jsYaml = { + Type: Type, + Schema: Schema, + FAILSAFE_SCHEMA: FAILSAFE_SCHEMA, + JSON_SCHEMA: JSON_SCHEMA, + CORE_SCHEMA: CORE_SCHEMA, + DEFAULT_SCHEMA: DEFAULT_SCHEMA, + load: load, + loadAll: loadAll, + dump: dump, + YAMLException: YAMLException, + types: types, + safeLoad: safeLoad, + safeLoadAll: safeLoadAll, + safeDump: safeDump, + }; + + exports.CORE_SCHEMA = CORE_SCHEMA; + exports.DEFAULT_SCHEMA = DEFAULT_SCHEMA; + exports.FAILSAFE_SCHEMA = FAILSAFE_SCHEMA; + exports.JSON_SCHEMA = JSON_SCHEMA; + exports.Schema = Schema; + exports.Type = Type; + exports.YAMLException = YAMLException; + exports.default = jsYaml; + exports.dump = dump; + exports.load = load; + exports.loadAll = loadAll; + exports.safeDump = safeDump; + exports.safeLoad = safeLoad; + exports.safeLoadAll = safeLoadAll; + exports.types = types; + + Object.defineProperty(exports, '__esModule', { value: true }); +}); diff --git a/routes/api/index.php b/routes/api/index.php index e69de29..482c6dd 100755 --- a/routes/api/index.php +++ b/routes/api/index.php @@ -0,0 +1,3 @@ +add('/api/system/logs', function () { include __DIR__ . '/../../api/System/logs.php'; }); + +$router->add('/api/system/getTranslation', function () { + include __DIR__ . '/../../api/System/getTranslationKey.php'; +}); diff --git a/routes/api/user.php b/routes/api/user.php index befd78d..af3b749 100755 --- a/routes/api/user.php +++ b/routes/api/user.php @@ -1,5 +1,7 @@ add('/api/user/doesinfoexist', function () { include __DIR__ . '/../../api/User/infoalreadyexists.php'; exit; diff --git a/routes/views/auth.php b/routes/views/auth.php index ff2cd04..b518061 100755 --- a/routes/views/auth.php +++ b/routes/views/auth.php @@ -2,6 +2,8 @@ use MythicalSystemsFramework\CloudFlare\TurnStile; +global $router; + $router->add('/auth/register', function () { /* * The requirement for each template diff --git a/routes/views/error.php b/routes/views/error.php index 468049a..d4d79b5 100755 --- a/routes/views/error.php +++ b/routes/views/error.php @@ -1,5 +1,7 @@ add('/errors/404', function () { /* * The requirement for each template diff --git a/routes/views/index.php b/routes/views/index.php index 5bd2275..9d556f1 100755 --- a/routes/views/index.php +++ b/routes/views/index.php @@ -1,5 +1,7 @@ add('/', function () { /* * The requirement for each template diff --git a/addons/Example/Example.php b/storage/addons/Example/Example.php similarity index 100% rename from addons/Example/Example.php rename to storage/addons/Example/Example.php diff --git a/addons/Example/MythicalFramework.json b/storage/addons/Example/MythicalFramework.json similarity index 100% rename from addons/Example/MythicalFramework.json rename to storage/addons/Example/MythicalFramework.json diff --git a/addons/data.json b/storage/addons/data.json similarity index 100% rename from addons/data.json rename to storage/addons/data.json diff --git a/caches/.gitkeep b/storage/caches/.gitkeep similarity index 100% rename from caches/.gitkeep rename to storage/caches/.gitkeep diff --git a/lang/en_US.php b/storage/lang/en_US.php similarity index 100% rename from lang/en_US.php rename to storage/lang/en_US.php diff --git a/docs/api/admin/.gitkeep b/storage/logs/.gitkeep similarity index 100% rename from docs/api/admin/.gitkeep rename to storage/logs/.gitkeep diff --git a/migrate/config/1.php b/storage/migrate/config/1.php similarity index 100% rename from migrate/config/1.php rename to storage/migrate/config/1.php diff --git a/migrate/config/2.php b/storage/migrate/config/2.php similarity index 100% rename from migrate/config/2.php rename to storage/migrate/config/2.php diff --git a/migrate/config/3.php b/storage/migrate/config/3.php similarity index 100% rename from migrate/config/3.php rename to storage/migrate/config/3.php diff --git a/migrate/config/4.php b/storage/migrate/config/4.php similarity index 100% rename from migrate/config/4.php rename to storage/migrate/config/4.php diff --git a/migrate/config/5.php b/storage/migrate/config/5.php similarity index 100% rename from migrate/config/5.php rename to storage/migrate/config/5.php diff --git a/migrate/config/6.php b/storage/migrate/config/6.php similarity index 100% rename from migrate/config/6.php rename to storage/migrate/config/6.php diff --git a/migrate/database/1.sql b/storage/migrate/database/1.sql similarity index 100% rename from migrate/database/1.sql rename to storage/migrate/database/1.sql diff --git a/migrate/database/10.sql b/storage/migrate/database/10.sql similarity index 100% rename from migrate/database/10.sql rename to storage/migrate/database/10.sql diff --git a/migrate/database/11.sql b/storage/migrate/database/11.sql similarity index 100% rename from migrate/database/11.sql rename to storage/migrate/database/11.sql diff --git a/migrate/database/12.sql b/storage/migrate/database/12.sql similarity index 100% rename from migrate/database/12.sql rename to storage/migrate/database/12.sql diff --git a/migrate/database/13.sql b/storage/migrate/database/13.sql similarity index 100% rename from migrate/database/13.sql rename to storage/migrate/database/13.sql diff --git a/migrate/database/14.sql b/storage/migrate/database/14.sql similarity index 100% rename from migrate/database/14.sql rename to storage/migrate/database/14.sql diff --git a/migrate/database/15.sql b/storage/migrate/database/15.sql similarity index 100% rename from migrate/database/15.sql rename to storage/migrate/database/15.sql diff --git a/migrate/database/16.sql b/storage/migrate/database/16.sql similarity index 100% rename from migrate/database/16.sql rename to storage/migrate/database/16.sql diff --git a/migrate/database/17.sql b/storage/migrate/database/17.sql similarity index 100% rename from migrate/database/17.sql rename to storage/migrate/database/17.sql diff --git a/migrate/database/18.sql b/storage/migrate/database/18.sql similarity index 100% rename from migrate/database/18.sql rename to storage/migrate/database/18.sql diff --git a/migrate/database/19.sql b/storage/migrate/database/19.sql similarity index 100% rename from migrate/database/19.sql rename to storage/migrate/database/19.sql diff --git a/migrate/database/2.sql b/storage/migrate/database/2.sql similarity index 100% rename from migrate/database/2.sql rename to storage/migrate/database/2.sql diff --git a/migrate/database/3.sql b/storage/migrate/database/3.sql similarity index 100% rename from migrate/database/3.sql rename to storage/migrate/database/3.sql diff --git a/migrate/database/4.sql b/storage/migrate/database/4.sql similarity index 100% rename from migrate/database/4.sql rename to storage/migrate/database/4.sql diff --git a/migrate/database/5.sql b/storage/migrate/database/5.sql similarity index 100% rename from migrate/database/5.sql rename to storage/migrate/database/5.sql diff --git a/migrate/database/6.sql b/storage/migrate/database/6.sql similarity index 100% rename from migrate/database/6.sql rename to storage/migrate/database/6.sql diff --git a/migrate/database/7.sql b/storage/migrate/database/7.sql similarity index 100% rename from migrate/database/7.sql rename to storage/migrate/database/7.sql diff --git a/migrate/database/8.sql b/storage/migrate/database/8.sql similarity index 100% rename from migrate/database/8.sql rename to storage/migrate/database/8.sql diff --git a/migrate/database/9.sql b/storage/migrate/database/9.sql similarity index 100% rename from migrate/database/9.sql rename to storage/migrate/database/9.sql diff --git a/settings.json b/storage/settings.json similarity index 63% rename from settings.json rename to storage/settings.json index e2c8fd6..73a91da 100755 --- a/settings.json +++ b/storage/settings.json @@ -1,5 +1,5 @@ { - "__last_updated": "-", + "__last_updated": "2024-08-19 21:10:55", "framework": { "version": "1.0.1", "branch": "develop", @@ -9,12 +9,15 @@ "database": { "host": "127.0.0.1", "port": "3306", - "username": "", + "username": "framework", "password": "", "name": "framework" }, "encryption": { "method": "MythicalCore", - "key": "" - } + "key": "0pDYJ1PoL6Mv1rLmjQHPikUgPIbuCCVFXUQDUXwwPjw=" + }, + "app": { + "maintenance": "false" } +} \ No newline at end of file diff --git a/styles.css b/storage/styles.css similarity index 100% rename from styles.css rename to storage/styles.css diff --git a/themes/default/auth/register.twig b/storage/themes/default/auth/register.twig similarity index 83% rename from themes/default/auth/register.twig rename to storage/themes/default/auth/register.twig index bec6541..af93b8c 100755 --- a/themes/default/auth/register.twig +++ b/storage/themes/default/auth/register.twig @@ -1,7 +1,6 @@ {% extends 'components/page.twig' %} {% block head %} - {% endblock %} {% block content %} @@ -28,11 +27,11 @@ {{ setting('app','name') }} -
+
- + @@ -47,7 +46,7 @@
- + @@ -62,7 +61,7 @@
- + @@ -77,7 +76,7 @@
- + @@ -114,8 +113,9 @@
-
{% endblock %}{% block footer %} - +
{% endblock %} +{% block footer %} + {% if isTurnStileEnabled == true %} diff --git a/themes/default/components/header.twig b/storage/themes/default/components/header.twig similarity index 99% rename from themes/default/components/header.twig rename to storage/themes/default/components/header.twig index 778d1a0..03decea 100755 --- a/themes/default/components/header.twig +++ b/storage/themes/default/components/header.twig @@ -31,7 +31,7 @@ - +
diff --git a/themes/default/components/page.twig b/storage/themes/default/components/page.twig similarity index 84% rename from themes/default/components/page.twig rename to storage/themes/default/components/page.twig index e622714..3733a6d 100755 --- a/themes/default/components/page.twig +++ b/storage/themes/default/components/page.twig @@ -13,17 +13,25 @@ - - - + + - {{ setting('app','name') }} - {{ page_name }} + + {{ setting('app','name') }} + - + {{ page_name }} {% block head %}{% endblock %} + + + + + {% include 'components/preloader.twig' %} @@ -70,6 +78,7 @@
- {% block footer %}{% endblock %} - - + + {% block footer %} + {% endblock %} + diff --git a/themes/default/components/preloader.twig b/storage/themes/default/components/preloader.twig similarity index 100% rename from themes/default/components/preloader.twig rename to storage/themes/default/components/preloader.twig diff --git a/themes/default/components/sidebar.twig b/storage/themes/default/components/sidebar.twig similarity index 100% rename from themes/default/components/sidebar.twig rename to storage/themes/default/components/sidebar.twig diff --git a/themes/default/errors/401.twig b/storage/themes/default/errors/401.twig similarity index 100% rename from themes/default/errors/401.twig rename to storage/themes/default/errors/401.twig diff --git a/themes/default/errors/403.twig b/storage/themes/default/errors/403.twig similarity index 100% rename from themes/default/errors/403.twig rename to storage/themes/default/errors/403.twig diff --git a/themes/default/errors/404.twig b/storage/themes/default/errors/404.twig similarity index 100% rename from themes/default/errors/404.twig rename to storage/themes/default/errors/404.twig diff --git a/themes/default/errors/500.twig b/storage/themes/default/errors/500.twig similarity index 100% rename from themes/default/errors/500.twig rename to storage/themes/default/errors/500.twig diff --git a/themes/default/index.twig b/storage/themes/default/index.twig similarity index 100% rename from themes/default/index.twig rename to storage/themes/default/index.twig