diff --git a/backend/additional_text.php b/backend/additional_text.php new file mode 100755 index 0000000..38e864a --- /dev/null +++ b/backend/additional_text.php @@ -0,0 +1,27 @@ + diff --git a/backend/common.php b/backend/common.php new file mode 100755 index 0000000..16c5f5e --- /dev/null +++ b/backend/common.php @@ -0,0 +1,77 @@ +&]/u', "", $string); + } + + // Get year + function getYear() { + + // Return year + return intval(date(DATE_YEAR_STRING)); + } + + // Starts with + function startsWith($haystack, $needle) { + + // Search backwards starting from haystack length characters from the end + return $needle === "" || mb_strrpos($haystack, $needle, -mb_strlen($haystack)) !== FALSE; + } +?> diff --git a/backend/language.php b/backend/language.php new file mode 100755 index 0000000..30ece23 --- /dev/null +++ b/backend/language.php @@ -0,0 +1,601 @@ + $availableLanguage) { + + // Check if available language has a currency constant + if(array_key_exists("Currency", $availableLanguage["Constants"]) === TRUE) { + + // Get available language's currency + $currency = $availableLanguage["Constants"]["Currency"]; + + // Check if currency isn't already in the list of language currencies + if(in_array($currency, $languageCurrencies, TRUE) === FALSE) + + // Append currency to list of language currencies + array_push($languageCurrencies, $currency); + } + } + + // Sort language currencies by currency name + usort($languageCurrencies, function($firstValue, $secondValue) { + + return strcoll($firstValue, $secondValue); + }); + } + + // Return language currencies + return $languageCurrencies; + } + + // Get translation contributors + function getTransactionContributors() { + + // Declare translation contributors + static $translationContributors; + + // Check if translation contributors doesn't exist + if(isset($translationContributors) === FALSE) { + + // Set translation contributors + $translationContributors = []; + + // Go through all available languages + foreach(getAvailableLanguages() as $languageIdentifier => $availableLanguage) { + + // Check if available language has contributors + if(array_key_exists("Contributors", $availableLanguage) === TRUE) { + + // Get available language's contributors + $contributors = $availableLanguage["Contributors"]; + + // Go through all contributors + foreach($contributors as $contributor => $link) { + + // Check if contributor doesn't have a link + if(is_int($contributor) === True) { + + // Check if contributor isn't already in the list of translation contributors + if(in_array($link, $translationContributors, TRUE) === FALSE) + + // Append contributor to list of translation contributors + array_push($translationContributors, $link); + } + + // Otherwise + else { + + // Check if contributor isn't already in the list of translation contributors + if(in_array($contributor, $translationContributors, TRUE) === FALSE) + + // Append contributor to list of translation contributors + $translationContributors[$link] = $contributor; + } + } + } + } + + // Sort translation contributors by contributor name + uasort($translationContributors, function($firstValue, $secondValue) { + + return strcoll($firstValue, $secondValue); + }); + } + + // Return translation contributors + return $translationContributors; + } + + // Get translated type value + function getTranslatedTypeValue($type, $value) { + + // Get language + $language = getLanguage(); + + // Check if type is text and value is a standalone placeholder + if($type === "Text" && preg_match(PLACEHOLDER_PATTERN, $value) === 1) { + + // Return value and the language used + return [ + + // Result + "Result" => $value, + + // Language + "Language" => $language + ]; + } + + // Otherwise + else { + + // Get available languages + $availableLanguages = getAvailableLanguages(); + + // Loop until a result is returned + while(TRUE) { + + // Check if language is available + if(array_key_exists($language, $availableLanguages) === TRUE) { + + // Check if specified type exist for the language and the specified value exists + if(array_key_exists($type, $availableLanguages[$language]) === TRUE && array_key_exists($value, $availableLanguages[$language][$type]) === TRUE) + + // Return value for the language and the language used + return [ + + // Result + "Result" => $availableLanguages[$language][$type][$value], + + // Language + "Language" => $language + ]; + + // Otherwise check if language provided a fallback language + else if($language !== DEFAULT_LANGUAGE && array_key_exists("Constants", $availableLanguages[$language]) === TRUE && array_key_exists("Fallback", $availableLanguages[$language]["Constants"]) === TRUE) + + // Set language to the language's fallback language + $language = $availableLanguages[$language]["Constants"]["Fallback"]; + + // Otherwise check if the language is not the default language + else if($language !== DEFAULT_LANGUAGE) + + // Set language to default language + $language = DEFAULT_LANGUAGE; + + // Otherwise + else { + + // Check if type is constants + if($type === "Constants") { + + // Return empty string and the language used + return [ + + // Result + "Result" => "", + + // Language + "Language" => $language + ]; + } + + // Otherwise assume type is text + else { + + // Return value and the language used + return [ + + // Result + "Result" => $value, + + // Language + "Language" => $language + ]; + } + } + } + + // Otherwise check if the language is not the default language + else if($language !== DEFAULT_LANGUAGE) + + // Set language to default language + $language = DEFAULT_LANGUAGE; + + // Otherwise + else { + + // Check if type is constants + if($type === "Constants") { + + // Return empty string and the language used + return [ + + // Result + "Result" => "", + + // Language + "Language" => $language + ]; + } + + // Otherwise assume type is text + else { + + // Return value and the language used + return [ + + // Result + "Result" => $value, + + // Language + "Language" => $language + ]; + } + } + } + } + } + + // Get constant + function getConstant($constant) { + + // Return translation for the specified constant + return getTranslatedTypeValue("Constants", $constant)["Result"]; + } + + // Get translation + function getTranslation($text, $arguments = []) { + + // Get translated text + $translatedText = getTranslatedTypeValue("Text", $text); + + // Go through all arguments + foreach($arguments as &$argument) { + + // Check if argument is a function + if(is_callable($argument) === TRUE) + + // Resolve the argument's value using the translated text's language + $argument = $argument($translatedText["Language"], $text); + } + + // Get formatted translation for the specified text + $formattedTranslation = vsprintf($translatedText["Result"], $arguments); + + // Check if formatting translation failed + if($formattedTranslation === FALSE) + + // Return empty string + return ""; + + // Otherwise + else + + // Return formatted translation + return $formattedTranslation; + } + + // Get default translation + function getDefaultTranslation($text) { + + // Return text + return $text; + } + + // Get number translation + function getNumberTranslation($number) { + + // Check if number isn't valid + if(is_string($number) === TRUE || is_numeric($number) === FALSE) { + + // Return function + return function($language, $value) { + + // Return empty string + return ""; + }; + } + + // Otherwise + else { + + // Return function + return function($language, $value) use ($number) { + + // Get available languages + $availableLanguages = getAvailableLanguages(); + + // Loop until a result is returned + while(TRUE) { + + // Check if language is available or value is a standalone placeholder + if(array_key_exists($language, $availableLanguages) === TRUE || preg_match(PLACEHOLDER_PATTERN, $value) === 1) { + + // Set number formatter + $numberFormatter = new NumberFormatter($language, NumberFormatter::DECIMAL); + + // Check if number formatter is valid and it can format using the provided language + if($numberFormatter !== FALSE && preg_replace('/_/u', "-", $numberFormatter->getLocale(Locale::VALID_LOCALE)) === $language) { + + // Configure number formatter + $numberFormatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, NUMBER_FORMAT_MINIMUM_FRACTION_DIGITS); + $numberFormatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, NUMBER_FORMAT_MAXIMUM_FRACTION_DIGITS); + $numberFormatter->setAttribute(NumberFormatter::GROUPING_USED, NUMBER_FORMAT_USE_GROUPING); + $numberFormatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_DOWN); + + // Return number formatted as the language + return $numberFormatter->format($number); + } + + // Otherwise check if language provided a fallback language + else if($language !== DEFAULT_LANGUAGE && array_key_exists("Constants", $availableLanguages[$language]) === TRUE && array_key_exists("Fallback", $availableLanguages[$language]["Constants"]) === TRUE) + + // Set language to the language's fallback language + $language = $availableLanguages[$language]["Constants"]["Fallback"]; + + // Otherwise check if the language is not the default language + else if($language !== DEFAULT_LANGUAGE) + + // Set language to default language + $language = DEFAULT_LANGUAGE; + + // Otherwise + else + + // Return number + return $number; + } + + // Otherwise check if the language is not the default language + else if($language !== DEFAULT_LANGUAGE) + + // Set language to default language + $language = DEFAULT_LANGUAGE; + + // Otherwise + else + + // Return number + return $number; + } + }; + } + } + + // Escape text + function escapeText($text) { + + // Return text with all escape characters escaped + return preg_replace('/' . ESCAPE_CHARACTER . '/u', ESCAPE_CHARACTER . ESCAPE_CHARACTER, $text); + } + + // Escape Data + function escapeData($array) { + + // Return array in JSON encoding with ampersands and single quotes encoded as HTML + return preg_replace('/\'/u', SINGLE_QUOTE_HTML_ENTITY, preg_replace('/&/u', AMPERSAND_HTML_ENTITY, json_encode($array))); + } + + + // Main function + + // Set locale + setlocale(LC_ALL, DEFAULT_LOCALE); +?> diff --git a/backend/resources.php b/backend/resources.php new file mode 100755 index 0000000..6cd474c --- /dev/null +++ b/backend/resources.php @@ -0,0 +1,1805 @@ + 0, + + // Y dimension + "Y Dimension" => 1, + + // File path + "File Path" => 2 + ]; + + // Favicons + const FAVICONS = [ + ["./favicon.ico"] + ]; + + // App icon parts + const APP_ICON_PARTS = [ + + // X dimension + "X Dimension" => 0, + + // Y dimension + "Y Dimension" => 1, + + // Use as favicon + "Use As Favicon" => 2, + + // Mobile only + "Mobile Only" => 3, + + // File path + "File Path" => 4 + ]; + + // App icons + const APP_ICONS = [ + [16, 16, TRUE, NULL, "./images/app_icons/app_icon-16x16.png"], + [24, 24, TRUE, NULL, "./images/app_icons/app_icon-24x24.png"], + [32, 32, TRUE, NULL, "./images/app_icons/app_icon-32x32.png"], + [48, 48, TRUE, NULL, "./images/app_icons/app_icon-48x48.png"], + [64, 64, TRUE, NULL, "./images/app_icons/app_icon-64x64.png"], + [114, 114, TRUE, NULL, "./images/app_icons/app_icon-114x114.png"], + [120, 120, TRUE, NULL, "./images/app_icons/app_icon-120x120.png"], + [128, 128, TRUE, NULL, "./images/app_icons/app_icon-128x128.png"], + [144, 144, TRUE, NULL, "./images/app_icons/app_icon-144x144.png"], + [152, 152, TRUE, NULL, "./images/app_icons/app_icon-152x152.png"], + [180, 180, TRUE, NULL, "./images/app_icons/app_icon-180x180.png"], + [192, 192, TRUE, FALSE, "./images/app_icons/app_icon-192x192.png"], + [192, 192, FALSE, TRUE, "./images/app_icons/app_icon-192x192-mobile.png"], + [256, 256, TRUE, FALSE, "./images/app_icons/app_icon-256x256.png"], + [256, 256, FALSE, TRUE, "./images/app_icons/app_icon-256x256-mobile.png"], + [384, 384, FALSE, NULL, "./images/app_icons/app_icon-384x384.png"], + [512, 512, FALSE, NULL, "./images/app_icons/app_icon-512x512.png"], + ["./images/app_icons/app_icon.svg"] + ]; + + // Touch icon parts + const TOUCH_ICON_PARTS = [ + + // X dimension + "X Dimension" => 0, + + // Y dimension + "Y Dimension" => 1, + + // File path + "File Path" => 2 + ]; + + // Touch icons + const TOUCH_ICONS = [ + [57, 57, "./images/touch_icons/touch_icon-57x57.png"], + [76, 76, "./images/touch_icons/touch_icon-76x76.png"], + [114, 114, "./images/touch_icons/touch_icon-114x114.png"], + [120, 120, "./images/touch_icons/touch_icon-120x120.png"], + [144, 144, "./images/touch_icons/touch_icon-144x144.png"], + [152, 152, "./images/touch_icons/touch_icon-152x152.png"], + [167, 167, "./images/touch_icons/touch_icon-167x167.png"], + [180, 180, "./images/touch_icons/touch_icon-180x180.png"], + ["./images/touch_icons/touch_icon-180x180.png"] + ]; + + // Tile image parts + const TILE_IMAGE_PARTS = [ + + // X dimension + "X Dimension" => 0, + + // Y dimension + "Y Dimension" => 1, + + // Ratio + "Ratio" => 2, + + // File path + "File Path" => 3 + ]; + + // Tile images + const TILE_IMAGES = [ + [70, 70, "square", "./images/tile_images/tile_image-70x70.png"], + [150, 150, "square", "./images/tile_images/tile_image-150x150.png"], + [310, 150, "wide", "./images/tile_images/tile_image-310x150.png"], + [310, 310, "square", "./images/tile_images/tile_image-310x310.png"] + ]; + + // Mask images + const MASK_IMAGE = "./images/mask_images/mask_image.svg"; + + // Theme color + const THEME_COLOR = "#FFFFFF"; + + // Background color + const BACKGROUND_COLOR = "#7A00D9"; + + // Files + $files = [ + "./" => [ + "Version" => 0, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./connection_test.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/font_awesome/font_awesome-5.15.4.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "gNULDPfQZDMqDI59ny4pTxq+0VxHZEywS5K3ha9GAbaDz9PGaMDvMd7jQoQAY+DDla5FNlAYSXG6mE7I7NMiOg==" + ], + "./fonts/font_awesome/font_awesome-5.15.4.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "ZLqsxHjeYNildxUCsvXMO5cIIhuWfamaIjlMuKEsY2q4pIQ1YjPCMrm9Df7hmdyMF4A+OKE8B+1dqmKpwB/rvQ==" + ], + "./fonts/font_awesome/font_awesome.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/font_awesome/font_awesome_solid-5.15.4.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qE4T8hbqlRRq8oWvmK7wtGTNliRA4WGhxgLKIXiheeBK5O0qL5jVsusWVIDsaSDg6I3nfV8et/Ee13Kwktr4ZQ==" + ], + "./fonts/font_awesome/font_awesome_solid-5.15.4.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "2RsYqSOpBz8fwf7PGwEJOPedgk0hB2Z1l/RaZdOEPjUOQguCqVBtfKYsAqWuq7HTZ2Y1+CXdN7hk0w8oNXFMQg==" + ], + "./fonts/open_sans/open_sans.css" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/open_sans/open_sans-1.10.woff" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "d3WdXIW6Z4EumIsy5FdtC8dPTlap7QuUhf5tjoM+NC/Rd60w5wXJJC0FhK8V9/aRQ9usHfNUPe/KldMpzrCcoA==" + ], + "./fonts/open_sans/open_sans-1.10.woff2" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1rOU7Kf82ZZRJvJiNGXlgypsFrwAnO2Pwz6y84jWZ3zuz7GLfVontK79sm35BPlBvDdvwFl/Yrl4ogXzPu3q6A==" + ], + "./fonts/open_sans/open_sans_semibold-1.10.woff" => [ + "Version" => 1, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "jW/+TDOsxdKHWZTSq5UOyKLPsrUlRyb6qN572yo/aCqgS9xsPZ5OBHi4GaMQDQOSmXvD9W6VwAa+6LuiFWthDg==" + ], + "./fonts/open_sans/open_sans_semibold-1.10.woff2" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "0JKBQKmvWErkWlPg4bfszONNBjfUWJWZhK6yr936YeLYfGGkM9DULs/7puQcUK8iEVC4+XZ3jhUP4B91flnInQ==" + ], + "./fonts/mwc/mwc.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/mwc/mwc.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "QvAe73bxFv4FTrIZQywgCrSFqgMP24xT56PjT0qkOd+Ic9s9jufOH/QAQIDFwbE6yZSEu28imxVw2H1I/f9rhA==" + ], + "./fonts/mwc/mwc.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "TxAB5YdnZoWzz/N9f+zGfa6KYTolOEXijnQuFtk762BOV/e0P97fx467xwzlB7/KR4klGmjpRxunpN1/F9l4Qw==" + ], + "./fonts/grin/grin.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/grin/grin.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "o2PiDYI0DXbX7wMWGPZLQhR6VnCwkLTX6iYholTb9pVzqhNc9vDiJRzNHqsaBNMr2oE//6IoTKAZzWqI39pZZg==" + ], + "./fonts/grin/grin.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "V6bq44MMOpge+H/UOcSRgZIsxrXh/aB9e02bRS+M5XWlbSppzXf+p0O08bEbPlMxWZaIbJCaI05zfA61rT2+gg==" + ], + "./fonts/epic/epic.css" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/epic/epic.woff" => [ + "Version" => 1, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "gNZ3w1GCnlgZoNwDzccpszVcV0GX4lDhO/6WxdXPNQMNDz/DT7sde4mffyp1E6jcdcafic6yqB24yBqpF54PIw==" + ], + "./fonts/epic/epic.woff2" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "b8IHIOXGhAp29uhKUNFwDgRjSfz2Q5kcGw9Bg/BJr3v8YSeXvLWtbZYzrh2LOMuwH18QQlhPecyqQj9akfm/EA==" + ], + "./fonts/btc/btc.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/btc/btc.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "SfSYyklmLxkhgoli6lw65pMyT9HUI5p8w8/d4ILwWrmiSyq7vg1wY2ZpAGdseTgHqCMFFHqRvVXgoxyPFO/Ixw==" + ], + "./fonts/btc/btc.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "HF1Vum3F2kBrrUKcZZ9VHiPRPzheZ/B8yahmr7aQx+TBCNeMtjYatU7N9lvqxWBiPo2cA6aYsyX2dzdljyxyuQ==" + ], + "./fonts/eth/eth.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./fonts/eth/eth.woff" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "gt/dON/VN9ECB6aNxi1438gJbLkpf2esYiMfuw/E78pS6+QyI/iR+coBhrEE7XOeU3Q4Xrp3eRk2FlRzbmBQRQ==" + ], + "./fonts/eth/eth.woff2" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "eo8uv656TWX4+oTTWrilXjsEsf0mY1wacVdsdskOHlw3CtZ0TtT6MwLig2m4LKHQFO11Ns/rZBtiw9DfGUXGCw==" + ], + "./robots.txt" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./privacy_policy.txt" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./favicon.ico" => [ + "Version" => 0, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./sitemap.xml" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/prices.js" => [ + "Version" => 16, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tJJnVOFB5KeDa13rKP9GYg71q8Ynwk+bNxSabqV2M2mCD8xu3GeACdF35TDd2WLTypXyZNp0IYhc5d1SjyfT5g==" + ], + "./scripts/log.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "v7LKQef3zZkFw6DEmH1rjipx7uQKkQntQapNGAbOv03yoRPoyUsOJERGrPE2KIhPxre6Dx1BlLAwrDAhMeslOQ==" + ], + "./scripts/qrcode-generator-1.4.4.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "OVFEJkHw515H4ocWALOe5Fg3GIJGJ3cOvWS5xPsoh75r6srpUB7B2C0pm9R1N/LerYefjk0eJDLR8pksUtyXwQ==" + ], + "./scripts/jsQR-1.4.0.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "H2kn8fvMTiODT1At1gNgDrzr+K+CS9k7ervx0YANJHUlCyGQhWHVI48TX6hmoDPFt1Xdi+vS1ej982fclk3ZEg==" + ], + "./scripts/clipboard.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "AaYCKtIK3CfkZgJL+KjqGHbvK+urVTVK0Wagvfi9MQcrVy95oT6vmwR0lkjIA1gO89vuQ15jrW4vjLyLjUihfA==" + ], + "./scripts/wallets.js" => [ + "Version" => 73, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "dPusPRCnCmHd3HtU3too2Th76vqqKBE3ARrdf3SIhznOinyyBXyvn+xDw9PoMWITeGeD5XVkUTuvXzPSQN0oag==" + ], + "./scripts/application.js" => [ + "Version" => 104, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "JxRayxeIgfzOSFJTzM/2N9XsKkgPH5otqTt6RqJw72b8V2/qfiSlacef4yqAV+p6tK/ZIrgKZ1IrTRHv0zoNnQ==" + ], + "./scripts/automatic_lock.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "CKe5Yj4y0545+GGmB5xPKsioR+2XwWy9dKa5H6l3wGQL6FPM84MQ6cif1kSPU2AQEEDP5iQdmUoTfjH7Nkvnkw==" + ], + "./scripts/wake_lock.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "6IoZ640jM4IsPVC0QB7+wiuens2+mcxLasLwGkGoszew+RVumYtcUmdruWbpr2LVG0ntS06TvUrUt5DqIJwVXQ==" + ], + "./scripts/language.js" => [ + "Version" => 64, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "VOxCDUjUJMIxmu56wmw6hqTKGRuq2QhyHmI/97+9xwN8Ax+xy3a4O6TTVyEdCsxEKHpUJSfDpnnvIv7+QcKzag==" + ], + "./scripts/protocol_handler.js" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "K87ooywiCyohMmt3YZeqByof/3bWyRpbYQJh+3ma78nHgRgGsdiaRI3qgnLS/M72Gq8jQKagdM1h7DrYqYv6sA==" + ], + "./scripts/copyright.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "LHfSJCXoGVnVigP9EA7rYdPy5ppdNINHp7Hm4+ZeKzLl+C0feXKPTMdc9j/7P4CVwt8UBKuX3ZYffIuiacVQuQ==" + ], + "./scripts/settings.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "/8I95gnj730sRL2t8+uapaIGhYl4n7lQHkrUa7nB2BTe73pIqJAkziCoabBBdcF+ClzPEuE1bVEOcjJ62nl99A==" + ], + "./scripts/caps_lock.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "k4sftkZ0gB3BKP7dmfcMmIzXcNOOgse3V0EK2aTqtx60SYxlEWtPf3wyD+TaMT4r5fQwdAWd/AWd/+LAwFAGhw==" + ], + "./scripts/instance.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "yBIcZfj1vFmK0dWgudrlqX3cYC0Hg+lrZ40uT3R6bfqZ7rAthgJTwkFjn+b5TXj+qV39KFJQJwKqnol7tIBdDA==" + ], + "./scripts/tetris.js" => [ + "Version" => 10, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "MkRkaXe8V7U6UZI/wJza3b8syfEflRaPzK6FS025whXZ3xCS+zeE8OuMlzYqCcZI4RURQ9ePc683eJrMAFdpnw==" + ], + "./scripts/logo.js" => [ + "Version" => 14, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "djV+b/HtH4v0vFscECm8/b8dj/sZ1Y4NrkHhb8FlUfZHAAKDpta2XMhC/QNrlBQoBJBIBjN/VOgnP9AoxmYd1Q==" + ], + "./scripts/service_worker_installer.js" => [ + "Version" => 10, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "lpFVNVY1nK6ZM1winGWXDkVOz4bRxgpgKbyC6YeJELwiAXSy32/pvYZMlaySJfHyRbeyRMahuHqGaRZUUjMQdA==" + ], + "./scripts/wallet.js" => [ + "Version" => 25, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "PojnUykqdVTAwgBtx2yYUzZLawnNQZS1AtSTHvMeImMo1X+5IFTVSY6Xezl8CTywaVkJ0SyRIWogmrOPvAmHvA==" + ], + "./scripts/consensus.js" => [ + "Version" => 30, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "n5ES3sazs7wZq1XqWZbRrk22pJ3r9fMQd9s4Z1J9HoWB5DV/OAS9PDK8qo8lgsLsar3aV9DTpf+RfIEfwN9lEQ==" + ], + "./scripts/transaction.js" => [ + "Version" => 13, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "D8F+g8KMqTxpFTXHYtZyJKNRmtsUx9vSIC/3+p3y/OoBRZ0SvB/HSmWPn9GgvJP5MJtWnsWaJDh0RCruLzK2OQ==" + ], + "./scripts/transactions.js" => [ + "Version" => 19, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "8A7HGyjR2cWp2dy5mtGX5+okEawJSsZxO71S4rUQeaweIhyLqR5y1TaH/EoR8QfCunEcY5QkNfm4P0TU8MWE3g==" + ], + "./scripts/focus.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "kKUstqzGQ5tu+0Mgeq+GaBOZAGC5hBxwPp1f/BhNKY7w0zV+xwz+Ko5AopwmK9EcQorimi2lkOTYCiXt7cSI+Q==" + ], + "./scripts/identifier.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "jSzvJoFNyGWGAlNp1a4r556E4qYOKEqZFy153hcWtYOjKYGsBLkY+z5koJK/kiDxemFjQ1v8HtfqeassdIiazg==" + ], + "./scripts/crypto.js" => [ + "Version" => 15, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1Dos1rNWtUlUuVq4ElvN9Twhy9SaeXeN3kTAGT7ND8tAPn3eskDtegm+zVMrxqH5iyN+Zk3cL7IbTKs23OtHKA==" + ], + "./scripts/api.js" => [ + "Version" => 131, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "la+Ke0z4OcOjZc0uhAP6w7pZ3Y+0esV/Gx5+ielneckX9kggeOfThy/sDFBrDF4+9lAWsA6logB27dAf9/iZ5Q==" + ], + "./scripts/hardware_wallet.js" => [ + "Version" => 77, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "jSkNynN4NGRO4TyTa7bz2ojp4oCCA64VeEGEqxqdi5U4CW9RySV3+6qJ/L7lVV/P3ODsvtpmcHR6MtMxBorOkg==" + ], + "./scripts/hardware_wallet_usb_transport.js" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "2Mv5CXrHhE4o7KnPHwme4X/CjM7CYl4WHxJ6c/7FA6jmCRjwP6xKr1OjoRmCrhiKJAmryK0pHSnQgjfKmbRoxA==" + ], + "./scripts/hardware_wallet_bluetooth_transport.js" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "btNPxYqBOouFkxCbzCuQWUoJqNvSXcsAF3IrNw1FpBQbLq1Ny3sIHj/Fqrz+c5C53JcGZ9Esgoffq9U5bMf23A==" + ], + "./scripts/hardware_wallet_definitions.js" => [ + "Version" => 10, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "EnU1TuygTMQWi1jyd8cKWL657kgmVrgi5rcn4PBQupeEl+WJKsKOVao8O7isPKvFygBYoQgPmQf3yTJM3O6beA==" + ], + "./scripts/protocol_buffers.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "lhVliQVhGnbjQIDWGz99uzyqBMQSwAYw31EoKfCBORKBgf2QrD+uqJzXazKSq7bUYE2Abd+g3j5CvZF5deTtmw==" + ], + "./scripts/proof_builder.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "+YQhxhPUHhCAaBEiVz7c3jiJL/eoGKcvhB8qi25Z2CrUdKIiSqdtrHAbIMLTlLmzwJ2iYoTxiAJdxxLxHJ5ryg==" + ], + "./scripts/legacy_proof_builder.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "z0SKIdDp9pej96Y6IWMsGAwTBYTMLywl8rkFhZn4uHxwtWr2zclalpxA6dzZZSUOaevd4w3SU68e9q3iWgCU/w==" + ], + "./scripts/new_proof_builder.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "HDbukxH9AKHtSVCYiFDJbf2nZ/2H/Vhl2PGTD81DUgX/m62mlsgwfk52Sg+S5RuXpx+gK6DXU3S8smegfyUi4g==" + ], + "./scripts/view_proof_builder.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "lxuV6f6wNme51ooebXyuRZ2ILRkvd48l57/9UcU9UB+SyB7hwzFGahoz3hQ9bNYbvCGf8tmjqv0M90hFi/3qEQ==" + ], + "./scripts/service_worker.js" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/bignumber.js-9.1.1.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "rc10/7RWHaBylfU5d1xX+pGO2dWTaooPdO7PbRmGSN8majqQGqDRoZ88xKUYfMK9oWp+m86MnRtvsx2CnmogDQ==" + ], + "./scripts/base64.js-3.7.5.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "i7T2cMTN4yMPw3QW8KMu48My+GLzRYpuJsA71Y5JJYYKJh+H8hjvycsVwkIeLZcpGbNbX+G4q6uZ7WuVEI61eA==" + ], + "./scripts/crc32-1.2.0.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1C/g5xU+C5AIbkigSJ53JJ9j83nFt/3A53atZUwYN615yngUk2Ur+ajqoTPqVjHaJzbkUsZFkLfgsR54KuGG7A==" + ], + "./scripts/database_transaction.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "xsBUDHmDFrkpT/ZXsyMdy9e0nls+PvNvl1ARxcAB8rPDWH3pH9x3PnLDCie7qmpaN+pOdYb6dxowQn2vcWHILA==" + ], + "./scripts/database.js" => [ + "Version" => 20, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "gArt5uC3V53OjPXQsCXwOGSceIpAa2M4Wcu7Pj46JTTevrFs7zq6+7LtmKBP/gPVB3OnexLq2Z5ietJ5N2riIw==" + ], + "./scripts/output.js" => [ + "Version" => 12, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "4+bncF10tkec8E6ZpuhaKLcLaFVYek0Ip34Fbc8/FtbM6lE9f5sQ8kfwaGifV8x3xsXY9doCywyrtM6KOxLzrw==" + ], + "./scripts/output_information.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "ig4BjvopYK4Oh7AtF9x5MntYRzW0oSh/n7BTqThp1DlC0vJCTyRJ+/mtOWtDdgC6U+5d4sJzMCBfmGm69sJNTQ==" + ], + "./scripts/output_worker.js" => [ + "Version" => 75, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/slate_worker.js" => [ + "Version" => 142, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/camera.js" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "2ZFvWf9x++Vv5+5yeq1fWEHazydcrD7rc+OTWhRAr3MlyZXYsKEvBo+/ZMHnBK7e5PPW/E34GSQDrgWXz+P0gA==" + ], + "./scripts/emoji.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "RFXyJdMBrFe0rBAKTOwmTVcZzI6V3Pf7HHizK+RQVHguh/pCOTPV0Jj+EB7cvP1/wpuUAyh1SnWVZ01qLg4emg==" + ], + "./scripts/languages.js" => [ + "Version" => 16, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/camera_worker.js" => [ + "Version" => 70, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./scripts/cookie_acceptance.js" => [ + "Version" => 9, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "UdiWWkqNvt3q0KH7I+E+eoFOWTt+hULNoXNxnwPaqevGInwqgpnjqJVG1S7lk9jo++XEPiJYK/xSWuwEwpEPRQ==" + ], + "./scripts/listener.js" => [ + "Version" => 21, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "n9X9WDCumLjb9pic1KPaTnqRmaxecFNn8AbZwCjboXJl4i3Fw03YeG0rDoyM5j+0K2Mme3H105EJy7+wj+pMow==" + ], + "./scripts/interaction.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "F6p/fp7lLTwzwxZI9R6YeP20ELp015vptQxEU6Y/4ryBpoYKuTJc6byBoeld6myKUkA+XzdC2ZMc92eiLVUUHg==" + ], + "./scripts/glMatrix-3.4.1.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "7oogIEuYjEdB9P08VXSyYmGIk6ltV1xlhXey/p/kjYaduDRLCFaLLK5AJi2pQy08vBR1Vs0Irn0FTCTEb9VqAA==" + ], + "./scripts/jQuery-3.6.4.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "6DC1eE3AWg1bgitkoaRM1lhY98PxbMIbhgYCGV107aZlyzzvaWCW1nJW2vDuYQm06hXrW0As6OGKcIaAVWnHJw==" + ], + "./scripts/js-sha3-0.8.0.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "QkO9sBiE1vi+hFY7yO9Zo4rIrgUHBydF77a3SfX5Rj+VqoA+XYrv/77n17Pki3IXnQHN2ReqxvUykN1sChUQTw==" + ], + "./scripts/js-sha256-0.10.0.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "FnKPFq1vQB0qyjsydpCBwfrPXAx8sC/ppUc/Xm3leT6rf4ei4tBiKOMoq76ilK/ZAHOKUmOzMA7GUin6IoN8sA==" + ], + "./scripts/ChaCha-2.1.0.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "53JqNBE9WxJ27yw3uyHCWE0kQ9DIxa9BMuY+JoTxImkqaxPD24y+FN3KNqL7johYZm/G5UQalzUJ/AvI7RBx2w==" + ], + "./scripts/json_rpc.js" => [ + "Version" => 16, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "n/aCB9fqFv7lGcxhOGiowafXf2dy683JipYk5I+qFMZPdJbTsYxXUypLc1P/ELVSQHpvMX87dMy1C95AHBMsEQ==" + ], + "./scripts/tor.js" => [ + "Version" => 10, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "ewMz7P/K/A/ej2daA/n6QeEr9h5A1aECP8eDr5B3L77R94uLQH4z07/W2WTsCZc1d7fRRk2oiGK50QNdSXrGpw==" + ], + "./scripts/mqs.js" => [ + "Version" => 18, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "QT8pp0Gth3Be2c7uJElPTbxP0wv3o8ilgLkeO1tCoDfSh9ke5mPxRKkCDtP+OKJoZNgU8qb/iEqYaSrTOiJr6A==" + ], + "./scripts/slatepack.js" => [ + "Version" => 19, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "aXh+5wy+nrqJmC2Z6UCp8owWg2C7zrxOFveLY3QCIkdpv9zOFnizZswJTSYhfp78SpK7EGNCfMoTT+CZe/tc6Q==" + ], + "./scripts/JSONBigNumber-1.1.1.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "yP+DgrnNWvBd6Wa/XTcmMkf+i+Ir0VyrxZ7rCrglwgL5j7shquvOGZ3fReEv09H+pZKMV3lvR5shnBncIo5VTQ==" + ], + "./scripts/BLAKE2b-0.0.2.wasm" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "4xU8Pp8t8FTYzuH07/RgDxdDSc708DTufixQvCArycHCqV5/GMmMTVn6x0METVE7zRqFnrLQGQAq7LOjjd5nNQ==" + ], + "./scripts/BLAKE2b-0.0.2.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "ZZpUPwazc+TKFvKShKqQYu9mda0g87afxz3v56jbRdw0sLZfr6V1UTKDlKpFtTpFPfB8MI6c4Slsrnu16ijsgg==" + ], + "./scripts/seed.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "z+60/pufhivW2lYUUNwAfvcx9HU5fRQG0uStMaosvaLDqHOmWTovgtKb5tu3k/q42goXcyT/aj2OwHJWV82lJQ==" + ], + "./scripts/secp256k1-zkp-0.0.29.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "099Zm36brdat2ZQVcbaBNxMD//jr4U5Sat5knVzPcMaC55asKBuZZ3ZL/RwaeUjeUKhKeFaBzhpFf04tyuv9Sg==" + ], + "./scripts/SMAZ-0.0.31.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "fO9ivOe5e/uRXI7Jej9akItWxQGq1JWKdDMmb4PRU47AiekyagJwfjxjp6x5FxTYAbpaEvbgx5i4KgYqw2k+pQ==" + ], + "./scripts/Ed25519-0.0.22.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "8z69nUefE0sJLexuhFr8X1Ljz/1vajNzWa+lYpX4438g+AJyUtZT/MGRpLWNnCzMt1iMM8Zi8MVmG3dydMWG1g==" + ], + "./scripts/X25519-0.0.23.js" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "Ba4AMi1Gkx7QB5Xa8OiafsoSjdA5zxaqG3WfyB1I4p9RumPDxl0ZAR21mVN5fPRxsnImSbkIiuU+Lwif40LvqQ==" + ], + "./scripts/tor_proxy.js" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "DKc9auFvln7lPN+3dXDaSpu/GASjOUb5TsUF/taDYvNDH9yftvtrRwtfISh5F3BOFqa3TIV8RqCljj5aF9ZORQ==" + ], + "./scripts/node.js" => [ + "Version" => 32, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "9cTe85/BnPewM6wINvwKGxieNFZ/X8FU8NKS9fLZpAymO3trEoRt2AflHDnDxYGanyMmA3FD4dOYctY2NvIyWg==" + ], + "./scripts/message.js" => [ + "Version" => 28, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "fUT2aWct3axErt/wI8N/bxKNkP3YMH9FkEYnKH4PRHhMKd90XDW8MIytywk4/OK2GMDexmxZoTv5irrRxmusjg==" + ], + "./scripts/common.js" => [ + "Version" => 101, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "Ci03nuBe0rpS/HKhQc9UnnP14r69uGawWkUHBD21+SNqKNwHe8WPTJDiHkSPc+PHMo76I8/SJJfjyLnfAl5Trw==" + ], + "./scripts/bit_reader.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "sZRRsnad/mXhGEl07hGYP/Ov+rf0+sbLcI1SSh8Na1b9E5m4edQYgpqr9W4PF0YfQdN+HqQZFPFQkQMyfAcpLw==" + ], + "./scripts/bit_writer.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "HPq8AJb3mxHLLkhUQpLCNbHRt/Xk7JwByYUQD8KCFBmI0UjSBAwvJOtEvPlivCg5AiRo7IKMwxUzUMQ+AQIwgw==" + ], + "./scripts/hi-base32-0.5.1.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "UADzbakt2KOLTZdSucmyNYI+VCgyiX/DfYIQDSU0GWrQJUfpLjBc9wUITrGx2oqdb+rvDuVXI0kzXowYoPXFPQ==" + ], + "./scripts/bech32-2.0.0.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "5zxFg82/Io72GE57rxH7nD3dewDWi8IDwV5RhtbNLzIyoBuZc/33MsG/ygYZwTnkRFUPHPYwcZkjBDzzYKPvvw==" + ], + "./scripts/base58.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1qSnBxDGofAzuph+6Z6WjRJ5TxiQAsURE9vtylnLOsB1OJYTUSYbffI8GQXLQ6n0LPusp2HxTN8i+4BKc31iIg==" + ], + "./scripts/fatal_error.js" => [ + "Version" => 18, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "aWPcZ8cSOyW0hKu7hOVlQrx4fwuZt5E6vftLCyOboHC1HTPUoizRoRtdLeGipTXLZ4dpGo6HmjanE4SFHPDDIQ==" + ], + "./scripts/scroll.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "3vXtreIWfA4ZmF19S6v8GgBTBQKNIyWusog09bGqpbxT17E6Fj/uKUW8NW9jVS3tysjqu6D5c2M6UFmDA60Wtw==" + ], + "./scripts/startup_images_creator.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "QNtKNG9TS2TfRgBAjPlLqCDIXv/UIcqDCTMi6tNwd3C7yxlKBkiErW/wtLpufToe46dfyTjUwbsBxkHAQilPBA==" + ], + "./scripts/extension.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "W4VU2BdG0lMevLGhEK8sx7aXj3EHoQIqfRQBjQ/MuannygiCmjsaWjoH84qSGSZdrB8rMmzkxte4vyrIKqRt2w==" + ], + "./scripts/version.js" => [ + "Version" => 13, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1drVCyRwFERSpHGYqKn/IMCeMUFYz/YmgbCecM8XazxOaRWEn4wSUXNF4LJOEBELoUyGr1aD4RHCkb1A90M/IQ==" + ], + "./scripts/secp256k1-zkp-0.0.29.wasm" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "cy6D8ymfNYx2nClB7IewZw/TkVQmLQW9Y2ioL0eQTyLVRGC4xUjH5nc/le1uTXq8fQhwqkcwnpN9vlFcZLWIow==" + ], + "./scripts/SMAZ-0.0.31.wasm" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tewJpZEMVKaHoTmFRoCc/J4P5pv1FWE4uv4iN7Wf1x5Bbz0llWlQ1anZsmfXJWH3mqKiZH9ChuvbKAC/PReZ+g==" + ], + "./scripts/Ed25519-0.0.22.wasm" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "unWNdFJP8XDQLIzNHRaCNzt1XP9u9zQMLwFkxL0BLkb6jd16angfAlGK2zLyv5DukC74edDQ+OSZa+HJl+iS9g==" + ], + "./scripts/X25519-0.0.23.wasm" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "aD21EXxdkP4IWbct7HkP1+KNqzcQM/vfm3kRRH3jzhpCOJ+uAS/WNAe8R83stTD1wQTYhVJhQcRLjh5UdeTsVw==" + ], + "./scripts/height.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "KsE/PDYuBECaR9jgFKQkGx2F7Qoj6GKVFusYUHQGPDFhoumYsflByyo0omTAq88hHlizrRjqn51OFUEeh8TY4w==" + ], + "./scripts/sections.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "VlwB5jd55CO2XVi+VBrYcXrEt65zFDs7IHn/pcrHebub8T79Rq9UE7FUnExUT5IAIeQSJJL/dctx3LRtQ/qjpA==" + ], + "./scripts/section.js" => [ + "Version" => 15, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "FuRkoTy9wjnIhWFbbhyA5ZX65tehALL+qyvu4vX2Djub7PKBpU2DB+SM6ZqTlOpJK6SEZD/GD9IhQnDurylU8w==" + ], + "./scripts/settings_section.js" => [ + "Version" => 25, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tbo37DKhDLWgAFFmdHYd7m4aqyqHIQev/OEROMgr0+2IK+Wp9ldX+dzkDsxJWV0zsF8F9aDCf9AAaOkXWkF4eg==" + ], + "./scripts/about_section.js" => [ + "Version" => 20, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "liNasQI9AlFyJZvge45UmsJ8mzflhC3CIOkRQAEqlO/JTscDe3edhvzHGZPrznwpqfCk9+pcWQSFXN8BLruXzw==" + ], + "./scripts/transaction_section.js" => [ + "Version" => 38, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "UDay3ItgLltbFNgSTX5T5LHkJ9fGq37t40XKjjBghcYBPG+IEuKzVfEZuqC0TXympEEvRmxfjQ9pRvqoK5W1tw==" + ], + "./scripts/account_section.js" => [ + "Version" => 14, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "NYI6dt8F95YbVXNa9QMby6C4L1ME8Ta+80FEZg5X4YQEn9N69du4R8yuCKDa+WWXFuxjcjokJHAYuW6hTnRguA==" + ], + "./scripts/wallet_section.js" => [ + "Version" => 95, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "jRN7pGzbJeD8lvmQ5ALAowBt9QUrQfXY4e0GNR+cS0evYDOsOOiNttLvZhsPZTnhzzGRpkpJ4JhSHla3uQh7lQ==" + ], + "./scripts/send_payment_section.js" => [ + "Version" => 106, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "6M0CZzIKGZEVngGgTyYxn+4FKCWpZwTKNvwhh3e18xlh1uTzBiVgszeL8U3KejwmMXIW+hSofztUH80cE87mjQ==" + ], + "./scripts/log_section.js" => [ + "Version" => 7, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "5OYi7Oy3IWIdHayxkF6PhVTlRkEkMAV2J15sZEEAgKjIMC7+379SuoTTChEq4Ec+s0B0NgsrpyMYTUfQkMjW2g==" + ], + "./scripts/initial_heights_obtained.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "RGtr0f7BvR7DyZRonM03t6BbKKNW9mjS4gmtF33pslBTR+cQL6WvbY0Q6t6/Mu/bsV6B+ywY5uEUU7jpP9n5pQ==" + ], + "./scripts/recent_heights.js" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "T5uHBo/NYcxx0o9wdsFoyFqmNa7evSYVkAOoSCXr6d+4BZ/N3CeXGxsMRCsrODROagZnW0gBQj87suvwKEBUMQ==" + ], + "./scripts/maintenance_notification.js" => [ + "Version" => 11, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "HX4BGD6ZkumCAIOraOfAoJUCnA7ne6vYJbMBnnaFtfbYQqnMUXMPfF1fXmpbfEc/NYI3hAX9q5NttgVidHp0OA==" + ], + "./scripts/install_app.js" => [ + "Version" => 14, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tL8JRuLipT2XAjuSFouO83j4CGsTPvdWE8Co6cKcuhskCdPKA3XquDvo3DgpU4cRhbjc4DrDwTZq27YChQ5IIA==" + ], + "./scripts/unlocked.js" => [ + "Version" => 92, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "82dzsYR3p2pahc9CDklC2bFTmPDINx/GDiQKiIelG1YFE2f2kSOi1Ga7gPTIk29oWImtHQAYcIRkPNIZE2KOAw==" + ], + "./scripts/uuid.js" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "FARFAds6eOs+YYMI35eRSx83h0XSayo3N2CSjbQ347zVBf8WHtnn6qU7J6OlqRQEH+1ioZoLY3xWPXZCh2qN6g==" + ], + "./scripts/hash.js" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "OA4SbseOEmVcPnIky32I8ZUCfD16z7CnRpym1AVwXWvHwF/EG2E8E4LnsM32YpMCiTM2due3JCbxDtJITdFcwg==" + ], + "./scripts/slate.js" => [ + "Version" => 102, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "EAy5J2zli5ohSClmJs2Vn1riGqcf4jyHUKmmZF4WMY+11pVeVxUoqwqLVU1JWGR7ThmTm/MO146suKxyxyWPqQ==" + ], + "./scripts/slate_participant.js" => [ + "Version" => 11, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "wKmD1YCevS3Z8PuUqXGdFdOaaLepN0cwNp4pVEiHPOsMxlpuyYKaz+LpZoLIwwj3x9zMQ1n4LylhE7ZCpy+Spg==" + ], + "./scripts/slate_input.js" => [ + "Version" => 10, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "rf/rEuF1UyqCuTaJ/HsA81AttbgyWZdaJOloAFLIIhqSfRQg1ehLzjsfz1LC3kOZeYfv9UZzvOGeme0EA9jaRQ==" + ], + "./scripts/slate_output.js" => [ + "Version" => 9, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "X59+k52UjlbX4xjY1iHKXvLws7Ph9E3HRmRwYZR9oi8gUPgnkdIROnTRheBGLP/fHQ7D5EBCzDBjYPJ8XEPIAw==" + ], + "./scripts/slate_kernel.js" => [ + "Version" => 9, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qIFIYEDgExFhyokDEsP+XjflpB83Gx78R+9UT1MHI4MZ/vw/eqaHAEzf5kgdr8zjnpaONrc2Fso0TgjGRDuQPA==" + ], + "./errors/500.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/401.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/403.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/504.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/503.html" => [ + "Version" => 0, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/error.html" => [ + "Version" => 0, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/502.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./errors/404.html" => [ + "Version" => 0, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./browserconfig.xml" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./images/circle.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "wyTgo5fmZYwVSdR9kPllluUfvPzUg/Zd7uRvNVPjaqJr7pDI1vNWHFBtfOqmsuBcNV9awXZpePut9gHVKMjCVA==" + ], + "./images/down_arrow.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "3b1PtmLda1UA6xh5WWstp67TGoV/ITmtQ8pcot0uocmGSHUvL46ABelYATKbLthfecI2snR5Ar3PgQqpfN5NDQ==" + ], + "./images/usb.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "fMph2Fc1GDBQ4oFcnEo4CH3DElezAPdDquywENXnu3iUTZyn8kHaMsgGFoF1HMwFo6/zlM7CGl0mmgqI5WbyuA==" + ], + "./images/bluetooth.svg" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "esQ21VaSlM4oh9CkdibD5uTfG5fNzduVxx6tOiGQaEAT0rWoBphqIvxuc3YJmKzyK0K188lszHrhuJtBG1bgBA==" + ], + "./images/whale.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "gCcaLJJatAtW+DX14/LMR0/bJQ2+Tn4w007UlVD/n6UVq6hQ+aBhFBODeFuPE63y3ebHIA2OXmvU369Jl0+75A==" + ], + "./images/ledger.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "V7elNFHZZC5VVZcrj3xm6PztYGhhNyz5EDhvQIDaZHqW49D5/XRp16uPOOcmGcAEcRSpU3I59qzkjCexiM4giQ==" + ], + "./images/trezor.svg" => [ + "Version" => 2, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "keEwhm7p0JcKUS+qpyTwCBWreAa4eXUf9x6alWBVx2hMe8B4GN5kEXwc8/zkoL8EGrmS1Wl73myh26hYgUoI1A==" + ], + "./images/countries/america.svg" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "6YsD9TAM6RKbXUBtqaqWUX9yme3VmqUpDgyXLX6GHvvOwhHja4CDBUJeTc9WQNYIMoImr5IGl6CajrDg52ylaQ==" + ], + "./images/countries/china.svg" => [ + "Version" => 2, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "cW+hGTINEt40IUJmxVL33jY1NVh32p8tTB4UYaiZSx/NiIhnIy0ox+a1ioNWSUS43X+mTqHu9BaOeSxkZaB2zQ==" + ], + "./images/countries/greece.svg" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "G77foDMhtN8Gh7XXBg/D5EJ9DwiWT//UvPqq9pfJcrj13u92Luwi2cYssnqqVwe4gRkRyoMOv8TX8iQniKN+aQ==" + ], + "./images/countries/germany.svg" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "TKf6oGmstm1C2dJhgQYarPXKunanjax9M92Wk+FVTGc9ty7lB3UCLpBV2gqJmKk22COauKqa83vQg6+1lQ7FnQ==" + ], + "./images/countries/netherlands.svg" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "9Mlm0FJ9BE+0uNazl1mggjzqHviixc5LO0lKPbwKQcOaY7+tGvL2OCyPPQ6+XSWrrOq4dc8FUPCMF0BoJUikXw==" + ], + "./images/countries/czech_republic.svg" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "rLBl0FvuqHetz0DTr1ZnACgRfTjyWXFI7SG44HnSTgBi6Xem9P6ys84bf5rfq2xC6R/Fpo/6EzEAAGk6omJ8nQ==" + ], + "./images/app_icons/app_icon-152x152.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "qZ5wSv7nlGZ/34dRFY70uButTCB3IFWvI9TNEPVb/I7fBiizjLfd14liKjlOaGIhLlkhcrXf1ZJ9jiPYDq4JGA==" + ], + "./images/app_icons/app_icon-64x64.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "Gm8PRw90oQAoT7ziuw9aTpq5NXkAU9IRrHGsmwDnBuF6fU8SvZbVrtHVqZdKFBoOiiiTZPPJ57hY5IWa3I1qvg==" + ], + "./images/tile_images/tile_image-70x70.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "uj+AdzNHPcPrmPwG4r06U4rwIlSuL0hmwG0p3TJ/2IybdI/uDUbNV12QUY+0+D9JFZX04jm9OH1VmRK1bwu72A==" + ], + "./images/tile_images/tile_image-150x150.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "gD4yr31lazkNynmrykbL74WdKF1dSBUU3Zpcpa3joirzrLsSjG4EFveMb6CRy9iQi0QlHCoNGLx5jyid7D1bYA==" + ], + "./images/tile_images/tile_image-310x310.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "c/rHxGePwbGtTB4jCqfpiWHhxeBxhJ3o15p2hJz4IisjjhyPwdpe40EWpmbY+qAoCCsYX5lFkcSfpiSj5RMQ/g==" + ], + "./images/app_icons/app_icon-114x114.png" => [ + "Version" => 5, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "U+JR1ScLTBa55D4kSeItOYDBPD0iVCrEmlWNAfl4n/yEsp7lEQtRpMOaPNtUcKGL54nKduAkjN1dh4lv/j0ERQ==" + ], + "./images/touch_icons/touch_icon-180x180.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "orB/G4EAseFJ+xDivo5SgtAaEg3TCiSRjjjqZ3IEBwqFObggpvmBNjlIfPK2VYHdcsU//xrE4XUvrsFsqlzypA==" + ], + "./images/touch_icons/touch_icon-144x144.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "T/jsBMu4qPCAkCZSFalH2q51ckHyeHnAAh2a6WOe7I9ueagN8IA4J3hnxAZTUJILm3Jjkzp3Ixz3v57nOhGcCg==" + ], + "./images/logo_small.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "i7+n9MbIQCXdfta6ITG9x+NfvaXw1HtWgOFpZD5abJPC9/kwQzjgDPSdwNUegywCyfvZBbPqkkENuwRE0svQ+Q==" + ], + "./images/touch_icons/touch_icon-57x57.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "7KiOW6LBbVa665HD5GqGvvCdcSx8x8NSFYqn/kScISnDLWq/p69Ub21sFiTFdYERwkLzg1jfku4ny3uiem1smQ==" + ], + "./images/tile_images/tile_image-310x150.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "ymJVYV0wF6wdYqsDw2CgWDBrzBp87YY4cI9yKljJyd7hVGOfvpwIjMdGlI5mnHJpkr8yP3fSERmw6ckq1roXog==" + ], + "./images/touch_icons/touch_icon-167x167.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "Ilbe8853D9Y0cVIt3Y3RTgxFA0/fFhEPseRxmja2ktun17r7qNlF+l7LTFogruqWOrek2rOhBcdwS3ev0RmGLQ==" + ], + "./images/touch_icons/touch_icon-120x120.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "CEvrLz28mgcUdKxuzeeLL2FxVH/GryBSGhMkOEskjOMeQb0hTXbFjnzv37PybGitREzatLUH6EhwCySqENlRnA==" + ], + "./images/app_icons/app_icon-384x384.png" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "brCLTAz5RA5lmdp8yeEadk1tVFZFvIEeeQoCeNZbOG4IgQLvQYm7GPov+FNiCElyflNPPRCRK24uGut1sEQoDg==" + ], + "./images/app_icons/app_icon-512x512.png" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "m5Yogh0xABSeFHkZBQGFUPfgSVYH7W7O3bhpN5YHpJyRAaOKRg+QXEMuZU3dpBWj/sikKNsGG6iZjBbfxLYWKA==" + ], + "./images/app_icons/app_icon.svg" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "xMR6vmv2Kvv952moj7vWjAeajvSKmmkbDblSDEgzK0co3R4JbTI+53uLybJAteIQNiWGCcRopw+x2DF4d2EwDA==" + ], + "./images/app_icons/app_icon-256x256.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "ao8ydhrkQSRm/Wn2xo9S0mWwMNRMr1kn2BIskWGAnlJzbMfrk6eZVv/Dsx1OvoGtYeJYSvI9ICTgfkldFPd98g==" + ], + "./images/app_icons/app_icon-192x192-mobile.png" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "/QOYqpPj4saqmVLKm5g/Ise7oRCiZwJ4SBszh8Uii8FXbH+NOSFBrLHzShWemuDcv8UACIdirudMEA+NRwVTBQ==" + ], + "./images/app_icons/app_icon-256x256-mobile.png" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "7s+vHVZDiclgTx11ZFM1IcJ2bflVTCNbJ5uXOy1i1WB6YmyJiju8j2Zrygp5WOadyHEkPRkhdaaH4kcbSG0BPA==" + ], + "./images/app_icons/app_icon-32x32.png" => [ + "Version" => 5, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "8/eMVhg6nGzmzd+fkZ7mlyQaLLy6VvmkXnJyzBPDz87/RX+e7oXcZbqYER5ShFh3mgxTF1hNMsgbqtWa19x2RA==" + ], + "./images/mask_images/mask_image.svg" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "jX46GRxl03e1+FF+xvNiXa2roRnLfQjCZsmjFfFqWXhVpDA8qfdPVlOPysXMX2SXSzGISkH77MM1hbm2/E/qoQ==" + ], + "./images/logo_big.svg" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "cwWAMRY3P0swCMogiglMzxJaEx1mqJNsc7l7OtP0DleoM9WLggNLidZ681tbhBc1hJL844a+7lKd0k9JyMKhvw==" + ], + "./images/app_icons/app_icon-120x120.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "f4vKlplWm1ehQT2zQpsc+rTAxsWDXy0izZo4/JJ8akRaUffsHs7AghCw0jaP7MWRWszMB+q7Y+8EoCCsatyacQ==" + ], + "./images/app_icons/app_icon-180x180.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "pFf0SFovITFfFqweiKhhdX2PIFhYu9QehYFgMbrnGxLMjOlTg0bsr1jvvOKe2LNv8O8HtEn14X89BiZISwpwWA==" + ], + "./images/touch_icons/touch_icon-152x152.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "NgxJ8T19FxWYQfH9YDLWJHxDdYj4KMFqUtkbNvoZpa5W6RC970KyPYtW3SD0WVSYE8WYYtH1jKHAOWyNY2rh/w==" + ], + "./images/touch_icons/touch_icon-114x114.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "a/oINidjWR5POHUQv0kQvpxVgTY9OCJBBGi6GVIohJ1iYnDnteZ5311kEHHytkAWEUK63sehCyYm47WTuf+11w==" + ], + "./images/app_icons/app_icon-192x192.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "mnKFx4RG0ZtdtIYJc/dKM5/sOmUGKHzRFIF110w5Mz5pVnJqMbBgeII2i5enx/g7tO3jutnQoeg8DjvK7YuMog==" + ], + "./images/app_icons/app_icon-48x48.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "XQMVcSfxOWy7a7qIs9TK41H5NboS2vHuVgLahNTeFHA1Q8Q+XKViHr2vX07Xtj837sCGzDics0BW+jiqyVYkYA==" + ], + "./images/app_icons/app_icon-16x16.png" => [ + "Version" => 8, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "Nww9cqb81YoNraFe6JdSInU5BlkkyqbtdaD9XtSpFZh5fnmLEGojQj2uMhvjVpV4v0JqQ+shvYtKflvXT6DsZA==" + ], + "./images/app_icons/app_icon-24x24.png" => [ + "Version" => 5, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "pJ4if6I3Ps8ItDYtTkruAAvaPUErNiwpKwH9JID9QVUjKrTWzM2eAS7WbDN1rdK3u0+CGGp+qdDsISvrRZ5hGg==" + ], + "./images/touch_icons/touch_icon-76x76.png" => [ + "Version" => 6, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "pPKWb8sFr+8zAPTNla5MC/qj2RaO5thoI37cIenbViYTPtaHx1axkgldQnukDF7+gs0g6NbXmflpB/BvsKFvMQ==" + ], + "./images/app_icons/app_icon-144x144.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "ERloWcoV1xJ7Z191mG1Mh58qsWr/59gPxD0j7lsWllTDi7M10aD/+MUoMS2hmqPUBJDNJYAra951bo7VFLaaAQ==" + ], + "./images/app_icons/app_icon-128x128.png" => [ + "Version" => 4, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "caTLD2jtqhTWumHiyKvGJUXxpi7cb75/wsd+c5dyNkbPf6ba3cqlz2wnN1OQASc9sOq3NWcIyZsh7A4g3is4Gw==" + ], + "./site.webmanifest" => [ + "Version" => 0, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./styles/cookie_acceptance.css" => [ + "Version" => 16, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1iKjwj2jOLDQ8DBxGYhVzQ18C9omSRmAOb+rulDTYu39iI+RAT80JfAwGzKe+Sd/qdojtOAaxqxUhB/LfZEkxA==" + ], + "./styles/language.css" => [ + "Version" => 9, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "GPY+XpKAx4Y4f88HZnxVt3ugNXm5oGzPK9u2sgGhwcdTFTQes2pXto8Ydjvnb3r6AnAjJxYKzZUoTB+rEg0UFg==" + ], + "./styles/normalize.css-8.0.1.css" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "oHDEc8Xed4hiW6CxD7qjbnI+B07vDdX7hEPTvn9pSZO1bcRqHp8mj9pyr+8RVC2GmtEfI2Bi9Ke9Ass0as+zpg==" + ], + "./styles/common.css" => [ + "Version" => 9, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tOhHVb/LcJeCK0whL1JGdLGcBmMd58cq40BHkJQYk6I75O72DzZOpPB7/wZMbsee0tSPo86DxSLI4/IiOVJKLA==" + ], + "./styles/unlocked.css" => [ + "Version" => 20, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./styles/sections.css" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "XD9ZdiMk+TvjGzPL9bShDP8BHp1n5SyqVAtRs3AreF6Nq+KXPASTM+yEASG0wbibhpJvqb4YEWyQadd4gM7D6Q==" + ], + "./styles/section.css" => [ + "Version" => 11, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => NULL + ], + "./styles/settings_section.css" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "xrlfUeqc9EIcXeWCUYozrJSCCpJbGSO+2XJgzH+RErsFp+7VfJFVPb9FriaQ4F3SXDp/7qkA1oxwgAdUEluVCg==" + ], + "./styles/about_section.css" => [ + "Version" => 48, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "dvA+g9SQuOEXZ1+7F5IrhdFiSH3S7BXH3pC/s9gtfiC3WArvxjo0paJbPcIwdYjK47HVi3HGtN6Scxr8/SCAnw==" + ], + "./styles/transaction_section.css" => [ + "Version" => 12, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "CQu4kK60uQl2b13ufaFGAn0SE+d1jgulF6jVTrcJWgqRUwJBiAtFfbPszflVpmna4TNcuvserMUVX4d0pCoV5g==" + ], + "./styles/account_section.css" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "+iLOox399ALnS2ocTTcoR8TeLA911Bq1xoTAcINCUU40rxukwsiTQUbPLWxukc4VqPRhUlbTiTLie5fVPL64+Q==" + ], + "./styles/wallet_section.css" => [ + "Version" => 25, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "BqtTeRBzz5OQVKD09LmOKeBhpXy9ddeoe3xyvguYYHrWzGPZiZXIxJtbX9r+vuWw7NM9IRJ5PnshbSpvmEqthA==" + ], + "./styles/send_payment_section.css" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "cLTRO/Np02fFkoWyUAthMQl+YfCpvddAlNPL9UkbGYnvd0XkgDeBtBYSTIz+6MyE6Q9oacC+BUChZs0pUa4u0w==" + ], + "./styles/log_section.css" => [ + "Version" => 6, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "TfYlbeTkuPz7GEzxlt0TnKkDuQBSxiypStGdp66j9EX+cU4yUAYSCsymF5QFymex3/D+Oh2thrv/ZxP73UHjkg==" + ], + "./styles/maintenance_notification.css" => [ + "Version" => 13, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "vv14gQxsFGZPqjytDa5qqNcSPaumivuxIPUusIzLDmFd5KWpHfKYG5Zn0NZCEICYNGYtYTlzMRkmnZQtQ4fMsw==" + ], + "./styles/install_app.css" => [ + "Version" => 11, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "FZSSSrCiHPbDC3QYv/7H/o4lGhZtX3P2CAx+2fdo5I6RRZ25o0iVcWixpAF31SR7AeyisQ5TALUanUftlhLzNQ==" + ], + "./styles/application.css" => [ + "Version" => 16, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "VvrR6ya9WDBdsAObUtWoKmfydp7/TnmV6KaH4/uAmrcT34mgNBwzPky+uOTTUr5ub4X9SgaYPno7R9xXVlq9HQ==" + ], + "./styles/message.css" => [ + "Version" => 30, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "iIM9l24e0L5gNTVxTRdpUpTqwvP9Zu7KPbpwSmGAN/NqTE6RVrWqP+UD4aL/gjUpfGa0jhCFKOuNJu+1VlB1LQ==" + ], + "./styles/tetris.css" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "fV8QPcheziketlQHDWmDsP2g7qN4CGmm+UyokQmE8FrKqjCkpgk68Ed7XHBp/OY3RwPoWaAdIGi8yjE+Qmnu3A==" + ], + "./styles/logo.css" => [ + "Version" => 8, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "he11vx/asU+kvHkYSNNVP3Ydb1qa7CE5hTB/o8gF33cJK0oT6pQfRnCWZbSr+hyDBwpsosfiSw8drEpypaBbLA==" + ], + "./shaders/logo.frag" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "uUjiE+dVO09wWQuctlP2VBwMfbfmAW/xuvqL0VAKfMrYMoyuEGTVUBLy0Hc8rWyND3paJLtFS4EjFtSk+AvBDw==" + ], + "./shaders/logo.vert" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "vSZoTodVV1zVsJ5XQHkdHfg0GYR5TX58MM/nSXMJ7p9TwaBjr/sNOcY6TISoa6Y1RBTWVczZM0jWs5nsO1u6IA==" + ], + "./models/mwc.json" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "lN13VlgTjnoJSA7/ribpU/cYIkv/I+Kae2eEbzpSk/zQ4J4IxoNlQ9cpcQYrIJRrLFe9QIprXNZgwsFlWdJqNg==" + ], + "./scripts/BLAKE2b license.txt" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qqcTSJHo3upbPBJR1exc31V0W9MoKKpCuSciuQ8Wr+hYVF+NmdYYCqdABSb2S84Xr8I1WkHeDMOzI5Fbtd5vHw==" + ], + "./scripts/base64.js license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "cY8GK2XA/Ti4mQ9Ds6YxaRdC8876FIZ+hlwu3u9gAA176EFezcvcDSdh+OOibVmoADinP+4kVS4a4TX0NHy8gg==" + ], + "./scripts/crc32 license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "+ZcrmzsP3IHjWOBoSb0idDvDXvHkWVzqR98/EZdANUoeDg8bJOmM1aS7KhdEWdGO/ejk5jSwbl0bT8lqaLTBIg==" + ], + "./scripts/bignumber.js license.txt" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "0TceHGsWk3ibsrAv8lI3NGV904M5XxzjfVFV0nyMGRxXgYBn2056ANUOkznCUXXzdAOirlm6m0c6M6kC1gDXYQ==" + ], + "./scripts/bech32 license.txt" => [ + "Version" => 1, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "AakMfPm2wq07yVmqIBnTO6Bj9zKALq7T+wRvLUPsBn1JZctun/MACmC0OxyLruV+GUX45DUxvBXG1Rcm6bd1XQ==" + ], + "./scripts/ChaCha license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "1C7ZACZXUPp8Zy+0C43TlyPbOWrskOluiBLoI8yaoSKS/zV3I632A6T9NXyXbQdby220iysum6I/vr/FLgUwPA==" + ], + "./images/countries/Country Flags license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "ZrTab6FbCrWIxUi3vCup9rtWi5Mq2eKxFBxuT/a8IKbEzLLvoG1FD0TajprEcClniJQLRcfbSY9AWMqZXFzdIA==" + ], + "./fonts/font_awesome/Font Awesome license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "nXcGEO5ZIMn3PLQtikE8+zxxBMnZNgNSDslF7MK5U5C+9vrYWSbJfId7yYDjIIMqGNloclGUPBQw6Yqi71sEcw==" + ], + "./scripts/glMatrix license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "HKES2T36EnbF88OT/UyWxQ8KDSnS3x8xvetmr38epBEBYG6aLoHxWUMFtFfwDUgf8mGa/cdSMe+4CaEAatXYrw==" + ], + "./scripts/hi-base32 license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "I93NWqGcrmBBppLx2N0ASJnB94ZCklj5OrZGqa7QzfzGCS1mEegXR+7Lnoohl9vb9uJatRAmmfAmbyjg36K0QA==" + ], + "./scripts/JSONBigNumber license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "VskaH5EuExNsaMwR4iexZhz0cGGtI6Oj7fVngEAQ9IBMI3aJtkrl4fJE/joj0pgYYKPPJx+hi2loA+Xv9VL3cQ==" + ], + "./scripts/jQuery license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "I//WQZK/IcsrwOx5Z5e2wHu5wBE6DS0Z9u8OX1Tp4AQuMICytxU4jfKhjFRTO7gyyS6bmB1zp+vC8PsvIR49GA==" + ], + "./scripts/jsQR license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "MGmvPgoZ1MR+vP43MnsFnRhitgp4CjS5vNLEKzBO++bT7TIcvR/73qvINTfwy4tK3u6qomK7dFdwpcpnFRnFLQ==" + ], + "./scripts/js-sha3 license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "xqluHqX7LN2vwHKVgNfFApwQ/D5GAfEM2rWtuLszEhYk+g2Nmk5Ket/YS4VwEwf+O5ZMnFkXW3jTAFVQt0fyAQ==" + ], + "./scripts/js-sha256 license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "+URz+C7K7G1agtqu6vjZUWLiNMxk6ZmrRNcxHs7pb1sbD5yMPsPp8VEiSBWUqkKPMcm/XiKsXL32ue6by7dDdg==" + ], + "./scripts/secp256k1-zkp license.txt" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qElcmcO63QPcv3OrJp1b98l+Ut/sPnm6deMY93mqyUOedv9oLvA5EG4JNEWZ/Yxfzvb68jfk69CiNlo4QVreSQ==" + ], + "./scripts/SMAZ license.txt" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qElcmcO63QPcv3OrJp1b98l+Ut/sPnm6deMY93mqyUOedv9oLvA5EG4JNEWZ/Yxfzvb68jfk69CiNlo4QVreSQ==" + ], + "./fonts/btc/BTC license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "U88z3axdRLn2IqBB8me89TynBFVfBIHuPonuulBQGcjR1QSm/+fr1qfR1dfSbPPaBSzGOzVA60JgznSpPPNYQQ==" + ], + "./fonts/eth/ETH license.txt" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "U88z3axdRLn2IqBB8me89TynBFVfBIHuPonuulBQGcjR1QSm/+fr1qfR1dfSbPPaBSzGOzVA60JgznSpPPNYQQ==" + ], + "./fonts/grin/GRIN license.txt" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "U88z3axdRLn2IqBB8me89TynBFVfBIHuPonuulBQGcjR1QSm/+fr1qfR1dfSbPPaBSzGOzVA60JgznSpPPNYQQ==" + ], + "./styles/normalize.css license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "pSezlYXaPnUcXgNY1uTcePkCVL/YagLTAJeajG2n48iVI2DfWfptvDVQDpERSLEOsChtHBE0tvsAnkab/IUDaQ==" + ], + "./fonts/open_sans/Open Sans license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "TMWhK/6YTApQv3lD4tcKlI1SDvQjZ3x3YpcHqs46lao3jSBd6SkQXWRGgGeecO8kSUebNgrUSJa3W6/tZmEycg==" + ], + "./scripts/qrcode-generator license.txt" => [ + "Version" => 3, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "tgy0QxH9sSKe59CiCWgPJyln99UoIiuedEgdGp35X76n19dEF/uYOUJ6BxPNa/uZGdnLmgcwn0+oBZcniIXsVA==" + ], + "./scripts/Ed25519 license.txt" => [ + "Version" => 4, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qqcTSJHo3upbPBJR1exc31V0W9MoKKpCuSciuQ8Wr+hYVF+NmdYYCqdABSb2S84Xr8I1WkHeDMOzI5Fbtd5vHw==" + ], + "./scripts/X25519 license.txt" => [ + "Version" => 5, + "Cache" => TRUE, + "Minified" => FALSE, + "Checksum" => "qElcmcO63QPcv3OrJp1b98l+Ut/sPnm6deMY93mqyUOedv9oLvA5EG4JNEWZ/Yxfzvb68jfk69CiNlo4QVreSQ==" + ], + "./images/down arrow license.txt" => [ + "Version" => 3, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "nXcGEO5ZIMn3PLQtikE8+zxxBMnZNgNSDslF7MK5U5C+9vrYWSbJfId7yYDjIIMqGNloclGUPBQw6Yqi71sEcw==" + ], + "./images/bluetooth license.txt" => [ + "Version" => 1, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "nXcGEO5ZIMn3PLQtikE8+zxxBMnZNgNSDslF7MK5U5C+9vrYWSbJfId7yYDjIIMqGNloclGUPBQw6Yqi71sEcw==" + ], + "./images/usb license.txt" => [ + "Version" => 1, + "Cache" => FALSE, + "Minified" => FALSE, + "Checksum" => "nXcGEO5ZIMn3PLQtikE8+zxxBMnZNgNSDslF7MK5U5C+9vrYWSbJfId7yYDjIIMqGNloclGUPBQw6Yqi71sEcw==" + ] + ]; + + // Attributions + const ATTRIBUTIONS = [ + "BLAKE2b WASM Wrapper" => [ + "URL" => "https://github.com/NicolasFlamel1/BLAKE2b-WASM-Wrapper", + "License Path" => "./scripts/BLAKE2b license.txt", + "License Type" => "MIT License" + ], + "base64.js" => [ + "URL" => "https://github.com/dankogai/js-base64", + "License Path" => "./scripts/base64.js license.txt", + "License Type" => "BSD 3-Clause \"New\" or \"Revised\" License" + ], + "bech32" => [ + "URL" => "https://github.com/bitcoinjs/bech32", + "License Path" => "./scripts/bech32 license.txt", + "License Type" => "MIT License" + ], + "bignumber.js" => [ + "URL" => "https://github.com/MikeMcl/bignumber.js", + "License Path" => "./scripts/bignumber.js license.txt", + "License Type" => "MIT License" + ], + "ChaCha" => [ + "URL" => "https://github.com/calvinmetcalf/chacha20poly1305", + "License Path" => "./scripts/ChaCha license.txt", + "License Type" => "MIT License" + ], + "Country Flags" => [ + "URL" => "https://www.countryflags.com", + "License Path" => "./images/countries/Country Flags license.txt", + "License Type" => "Country Flags Non-Commercial License" + ], + "crc32" => [ + "URL" => "https://github.com/SheetJS/js-crc32", + "License Path" => "./scripts/crc32 license.txt", + "License Type" => "Apache License Version 2.0" + ], + "Ed25519 WASM Wrapper" => [ + "URL" => "https://github.com/NicolasFlamel1/Ed25519-WASM-Wrapper", + "License Path" => "./scripts/Ed25519 license.txt", + "License Type" => "MIT License" + ], + "Font Awesome" => [ + "URL" => "https://fontawesome.com", + "License Path" => "./fonts/font_awesome/Font Awesome license.txt", + "License Type" => "Font Awesome Free License" + ], + "glMatrix" => [ + "URL" => "https://github.com/toji/gl-matrix", + "License Path" => "./scripts/glMatrix license.txt", + "License Type" => "MIT License" + ], + "hi-base32" => [ + "URL" => "https://github.com/emn178/hi-base32", + "License Path" => "./scripts/hi-base32 license.txt", + "License Type" => "MIT License" + ], + "JSONBigNumber" => [ + "URL" => "https://github.com/wbuss/JSONBigNumber", + "License Path" => "./scripts/JSONBigNumber license.txt", + "License Type" => "MIT License" + ], + "jQuery" => [ + "URL" => "https://github.com/jquery/jquery", + "License Path" => "./scripts/jQuery license.txt", + "License Type" => "MIT License" + ], + "jsQR" => [ + "URL" => "https://github.com/cozmo/jsQR", + "License Path" => "./scripts/jsQR license.txt", + "License Type" => "Apache License Version 2.0" + ], + "js-sha256" => [ + "URL" => "https://github.com/emn178/js-sha256", + "License Path" => "./scripts/js-sha256 license.txt", + "License Type" => "MIT License" + ], + "js-sha3" => [ + "URL" => "https://github.com/emn178/js-sha3", + "License Path" => "./scripts/js-sha3 license.txt", + "License Type" => "MIT License" + ], + "Noto" => [ + "URL" => "https://fonts.google.com/noto", + "License Path" => "./fonts/btc/BTC license.txt", + "License Type" => "SIL Open Font License Version 1.1" + ], + "normalize.css" => [ + "URL" => "https://github.com/necolas/normalize.css", + "License Path" => "./styles/normalize.css license.txt", + "License Type" => "MIT License" + ], + "Open Sans" => [ + "URL" => "https://fonts.google.com/specimen/Open+Sans", + "License Path" => "./fonts/open_sans/Open Sans license.txt", + "License Type" => "Apache License Version 2.0" + ], + "qrcode-generator" => [ + "URL" => "https://github.com/kazuhikoarase/qrcode-generator", + "License Path" => "./scripts/qrcode-generator license.txt", + "License Type" => "MIT License" + ], + "Secp256k1-zkp WASM Wrapper" => [ + "URL" => "https://github.com/NicolasFlamel1/Secp256k1-zkp-WASM-Wrapper", + "License Path" => "./scripts/secp256k1-zkp license.txt", + "License Type" => "MIT License" + ], + "SMAZ WASM Wrapper" => [ + "URL" => "https://github.com/NicolasFlamel1/SMAZ-WASM-Wrapper", + "License Path" => "./scripts/SMAZ license.txt", + "License Type" => "MIT License" + ], + "X25519 WASM Wrapper" => [ + "URL" => "https://github.com/NicolasFlamel1/X25519-WASM-Wrapper", + "License Path" => "./scripts/X25519 license.txt", + "License Type" => "MIT License" + ] + ]; + + + // Main function + + // Check if disabling file versions + if(array_key_exists("NO_FILE_VERSIONS", $_SERVER) === TRUE) { + + // Go through all files + foreach($files as &$file) { + + // Set that file doesn't have a version + $file["Version"] = 0; + } + } + + // Check if disabling file checksums + if(array_key_exists("NO_FILE_CHECKSUMS", $_SERVER) === TRUE) { + + // Go through all files + foreach($files as &$file) { + + // Set that file doesn't have a checksum + $file["Checksum"] = NULL; + } + } + + // Check if disabling minified files + if(array_key_exists("NO_MINIFIED_FILES", $_SERVER) === TRUE) { + + // Go through all files + foreach($files as &$file) { + + // Set that file isn't minified + $file["Minified"] = FALSE; + } + } + + + // Supporting function implementation + + // Add minified suffix + function addMinifiedSuffix($file) { + + // Get file's suffix offset + $suffixOffset = mb_strrpos($file, "."); + + // Check if file contains no suffix + if($suffixOffset === FALSE || $suffixOffset < mb_strlen("./")) + + // Return file with minified suffix at the end + return $file . ".min"; + + // Otherwise + else + + // Return file with minified suffix insert before its suffix + return mb_substr($file, 0, $suffixOffset) . ".min" . mb_substr($file, $suffixOffset); + } + + // Get resource + function getResource($file) { + + // Use files + global $files; + + // Return resource with version + return ((array_key_exists($file, $files) === TRUE && $files[$file]["Minified"] === TRUE) ? addMinifiedSuffix($file) : $file) . ((array_key_exists("NO_FILE_VERSIONS", $_SERVER) === FALSE && array_key_exists($file, $files) === TRUE && $files[$file]["Version"] !== 0) ? "?" . $files[$file]["Version"] : ""); + } + + // Get checksum + function getChecksum($file) { + + // Use files + global $files; + + // Return checksum + return (array_key_exists("NO_FILE_CHECKSUMS", $_SERVER) === FALSE && array_key_exists($file, $files) === TRUE && $files[$file]["Checksum"] !== NULL) ? "sha512-" . $files[$file]["Checksum"] : ""; + } +?> diff --git a/browserconfig.xml b/browserconfig.xml new file mode 100755 index 0000000..475533d --- /dev/null +++ b/browserconfig.xml @@ -0,0 +1,28 @@ + + + + "; + ?> + + + + + diff --git a/connection_test.html b/connection_test.html new file mode 100755 index 0000000..e69de29 diff --git a/errors/401.html b/errors/401.html new file mode 100755 index 0000000..933d531 --- /dev/null +++ b/errors/401.html @@ -0,0 +1,36 @@ + diff --git a/errors/403.html b/errors/403.html new file mode 100755 index 0000000..985d30b --- /dev/null +++ b/errors/403.html @@ -0,0 +1,36 @@ + diff --git a/errors/404.html b/errors/404.html new file mode 100755 index 0000000..0bb107e --- /dev/null +++ b/errors/404.html @@ -0,0 +1,23 @@ + diff --git a/errors/500.html b/errors/500.html new file mode 100755 index 0000000..22ea2a0 --- /dev/null +++ b/errors/500.html @@ -0,0 +1,36 @@ + diff --git a/errors/502.html b/errors/502.html new file mode 100755 index 0000000..7355ca3 --- /dev/null +++ b/errors/502.html @@ -0,0 +1,36 @@ + diff --git a/errors/503.html b/errors/503.html new file mode 100755 index 0000000..3023764 --- /dev/null +++ b/errors/503.html @@ -0,0 +1,90 @@ + diff --git a/errors/504.html b/errors/504.html new file mode 100755 index 0000000..1aea5e8 --- /dev/null +++ b/errors/504.html @@ -0,0 +1,36 @@ + diff --git a/errors/error.html b/errors/error.html new file mode 100755 index 0000000..6619791 --- /dev/null +++ b/errors/error.html @@ -0,0 +1,64 @@ + diff --git a/errors/proxy_error_cors_disabled.html b/errors/proxy_error_cors_disabled.html new file mode 100755 index 0000000..e69de29 diff --git a/errors/proxy_error_cors_enabled.html b/errors/proxy_error_cors_enabled.html new file mode 100755 index 0000000..e69de29 diff --git a/errors/template.php b/errors/template.php new file mode 100755 index 0000000..255e9e6 --- /dev/null +++ b/errors/template.php @@ -0,0 +1,1493 @@ + +"> + + + + + + + + "> + + + + " type="image/svg+xml"> + " type="image/svg+xml"> + " type="font/woff2" crossorigin="anonymous"> + " type="font/woff2" crossorigin="anonymous"> + + " integrity=""> + " integrity=""> + " integrity=""> + + + + + + + + + + + + " crossorigin="use-credentials"> + "> + + + + + + + <?= encodeString(getTranslation($title, [getNumberTranslation($titleArgument)])); ?> + + + + "> + + + + + + COPYRIGHT_YEAR) { + + // Display copyright information with the current year + echo ""; + echo ""; + } + + // Otherwise + else { + + // Display copyright information with the copyright year + echo ""; + echo ""; + } + + // Display theme color + echo ""; + + // Check if not crawler + if($isCrawler === FALSE) { + + // Go through all touch icons + foreach(TOUCH_ICONS as $touchIcon) { + + // Check if touch icon's parts aren't provided + if(count($touchIcon) !== count(TOUCH_ICON_PARTS)) + + // Display touch icon + echo ""; + + // Otherwise + else + + // Display touch icon + echo ""; + } + } + + // Go through all favicons + foreach(FAVICONS as $favicon) { + + // Check if favicon's parts aren't provided + if(count($favicon) !== count(FAVICON_PARTS)) + + // Display favicon + echo ""; + + // Otherwise + else + + // Display favicon + echo ""; + } + + // Go through all app icons + foreach(APP_ICONS as $appIcon) { + + // Check if app icon's parts aren't provided + if(count($appIcon) !== count(APP_ICON_PARTS)) + + // Display app icon + echo ""; + + // Otherwise check if app icon can be used as a favicon + else if($appIcon[APP_ICON_PARTS["Use As Favicon"]] === TRUE) + + // Display app icon + echo ""; + } + + // Go through all tile images + foreach(TILE_IMAGES as $tileImage) + + // Display tile image + echo ""; + ?> + + + +
$availableLanguage) { + + // Display available language button + echo ""; + } + ?>

" type="font/woff2" crossorigin="anonymous">" type="font/woff2" crossorigin="anonymous">" integrity="">" integrity=""> + diff --git a/favicon.ico b/favicon.ico new file mode 100755 index 0000000..3f61079 Binary files /dev/null and b/favicon.ico differ diff --git a/fonts/btc/BTC license.txt b/fonts/btc/BTC license.txt new file mode 100755 index 0000000..d952d62 --- /dev/null +++ b/fonts/btc/BTC license.txt @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/btc/btc.css b/fonts/btc/btc.css new file mode 100755 index 0000000..4596b70 --- /dev/null +++ b/fonts/btc/btc.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "BTC"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} diff --git a/fonts/btc/btc.woff b/fonts/btc/btc.woff new file mode 100755 index 0000000..82fa303 Binary files /dev/null and b/fonts/btc/btc.woff differ diff --git a/fonts/btc/btc.woff2 b/fonts/btc/btc.woff2 new file mode 100755 index 0000000..7e027a6 Binary files /dev/null and b/fonts/btc/btc.woff2 differ diff --git a/fonts/epic/epic.css b/fonts/epic/epic.css new file mode 100755 index 0000000..8662eb0 --- /dev/null +++ b/fonts/epic/epic.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "EPIC"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} diff --git a/fonts/epic/epic.woff b/fonts/epic/epic.woff new file mode 100755 index 0000000..61ee740 Binary files /dev/null and b/fonts/epic/epic.woff differ diff --git a/fonts/epic/epic.woff2 b/fonts/epic/epic.woff2 new file mode 100755 index 0000000..55f99fc Binary files /dev/null and b/fonts/epic/epic.woff2 differ diff --git a/fonts/eth/ETH license.txt b/fonts/eth/ETH license.txt new file mode 100755 index 0000000..d952d62 --- /dev/null +++ b/fonts/eth/ETH license.txt @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/eth/eth.css b/fonts/eth/eth.css new file mode 100755 index 0000000..4759336 --- /dev/null +++ b/fonts/eth/eth.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "ETH"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} diff --git a/fonts/eth/eth.woff b/fonts/eth/eth.woff new file mode 100755 index 0000000..5bde05a Binary files /dev/null and b/fonts/eth/eth.woff differ diff --git a/fonts/eth/eth.woff2 b/fonts/eth/eth.woff2 new file mode 100755 index 0000000..f9c809e Binary files /dev/null and b/fonts/eth/eth.woff2 differ diff --git a/fonts/font_awesome/Font Awesome license.txt b/fonts/font_awesome/Font Awesome license.txt new file mode 100755 index 0000000..f31bef9 --- /dev/null +++ b/fonts/font_awesome/Font Awesome license.txt @@ -0,0 +1,34 @@ +Font Awesome Free License +------------------------- + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) +In the Font Awesome Free download, the CC BY 4.0 license applies to all icons +packaged as SVG and JS file types. + +# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +# Code: MIT License (https://opensource.org/licenses/MIT) +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +# Attribution +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +# Brand Icons +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/fonts/font_awesome/font_awesome-5.15.4.woff b/fonts/font_awesome/font_awesome-5.15.4.woff new file mode 100755 index 0000000..ad077c6 Binary files /dev/null and b/fonts/font_awesome/font_awesome-5.15.4.woff differ diff --git a/fonts/font_awesome/font_awesome-5.15.4.woff2 b/fonts/font_awesome/font_awesome-5.15.4.woff2 new file mode 100755 index 0000000..5632894 Binary files /dev/null and b/fonts/font_awesome/font_awesome-5.15.4.woff2 differ diff --git a/fonts/font_awesome/font_awesome.css b/fonts/font_awesome/font_awesome.css new file mode 100755 index 0000000..04f0574 --- /dev/null +++ b/fonts/font_awesome/font_awesome.css @@ -0,0 +1,30 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "Font Awesome"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} + +@font-face { + font-family: "Font Awesome"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: bold; + font-style: normal; + font-display: block; +} diff --git a/fonts/font_awesome/font_awesome_solid-5.15.4.woff b/fonts/font_awesome/font_awesome_solid-5.15.4.woff new file mode 100755 index 0000000..23ee663 Binary files /dev/null and b/fonts/font_awesome/font_awesome_solid-5.15.4.woff differ diff --git a/fonts/font_awesome/font_awesome_solid-5.15.4.woff2 b/fonts/font_awesome/font_awesome_solid-5.15.4.woff2 new file mode 100755 index 0000000..2217164 Binary files /dev/null and b/fonts/font_awesome/font_awesome_solid-5.15.4.woff2 differ diff --git a/fonts/grin/GRIN license.txt b/fonts/grin/GRIN license.txt new file mode 100755 index 0000000..d952d62 --- /dev/null +++ b/fonts/grin/GRIN license.txt @@ -0,0 +1,92 @@ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/grin/grin.css b/fonts/grin/grin.css new file mode 100755 index 0000000..450d29d --- /dev/null +++ b/fonts/grin/grin.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "GRIN"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} diff --git a/fonts/grin/grin.woff b/fonts/grin/grin.woff new file mode 100755 index 0000000..a2e15b9 Binary files /dev/null and b/fonts/grin/grin.woff differ diff --git a/fonts/grin/grin.woff2 b/fonts/grin/grin.woff2 new file mode 100755 index 0000000..6f57570 Binary files /dev/null and b/fonts/grin/grin.woff2 differ diff --git a/fonts/mwc/mwc.css b/fonts/mwc/mwc.css new file mode 100755 index 0000000..82282a0 --- /dev/null +++ b/fonts/mwc/mwc.css @@ -0,0 +1,22 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "MWC"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: block; +} diff --git a/fonts/mwc/mwc.woff b/fonts/mwc/mwc.woff new file mode 100755 index 0000000..cf13cd7 Binary files /dev/null and b/fonts/mwc/mwc.woff differ diff --git a/fonts/mwc/mwc.woff2 b/fonts/mwc/mwc.woff2 new file mode 100755 index 0000000..f7ce657 Binary files /dev/null and b/fonts/mwc/mwc.woff2 differ diff --git a/fonts/open_sans/Open Sans license.txt b/fonts/open_sans/Open Sans license.txt new file mode 100755 index 0000000..75b5248 --- /dev/null +++ b/fonts/open_sans/Open Sans license.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/fonts/open_sans/open_sans-1.10.woff b/fonts/open_sans/open_sans-1.10.woff new file mode 100755 index 0000000..ec7fa86 Binary files /dev/null and b/fonts/open_sans/open_sans-1.10.woff differ diff --git a/fonts/open_sans/open_sans-1.10.woff2 b/fonts/open_sans/open_sans-1.10.woff2 new file mode 100755 index 0000000..3bf8abc Binary files /dev/null and b/fonts/open_sans/open_sans-1.10.woff2 differ diff --git a/fonts/open_sans/open_sans.css b/fonts/open_sans/open_sans.css new file mode 100755 index 0000000..3f7662b --- /dev/null +++ b/fonts/open_sans/open_sans.css @@ -0,0 +1,30 @@ +@charset "UTF-8"; + + +@font-face { + font-family: "Open Sans"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Open Sans"; + src: url("../.") format("woff2"), url("../.") format("woff"); + font-weight: bold; + font-style: normal; + font-display: swap; +} diff --git a/fonts/open_sans/open_sans_semibold-1.10.woff b/fonts/open_sans/open_sans_semibold-1.10.woff new file mode 100755 index 0000000..205d312 Binary files /dev/null and b/fonts/open_sans/open_sans_semibold-1.10.woff differ diff --git a/fonts/open_sans/open_sans_semibold-1.10.woff2 b/fonts/open_sans/open_sans_semibold-1.10.woff2 new file mode 100755 index 0000000..3e088da Binary files /dev/null and b/fonts/open_sans/open_sans_semibold-1.10.woff2 differ diff --git a/images/app_icons/app_icon-114x114.png b/images/app_icons/app_icon-114x114.png new file mode 100755 index 0000000..61ffe16 Binary files /dev/null and b/images/app_icons/app_icon-114x114.png differ diff --git a/images/app_icons/app_icon-120x120.png b/images/app_icons/app_icon-120x120.png new file mode 100755 index 0000000..36ab1b7 Binary files /dev/null and b/images/app_icons/app_icon-120x120.png differ diff --git a/images/app_icons/app_icon-128x128.png b/images/app_icons/app_icon-128x128.png new file mode 100755 index 0000000..153fec4 Binary files /dev/null and b/images/app_icons/app_icon-128x128.png differ diff --git a/images/app_icons/app_icon-144x144.png b/images/app_icons/app_icon-144x144.png new file mode 100755 index 0000000..fddf702 Binary files /dev/null and b/images/app_icons/app_icon-144x144.png differ diff --git a/images/app_icons/app_icon-152x152.png b/images/app_icons/app_icon-152x152.png new file mode 100755 index 0000000..9cd73b0 Binary files /dev/null and b/images/app_icons/app_icon-152x152.png differ diff --git a/images/app_icons/app_icon-16x16.png b/images/app_icons/app_icon-16x16.png new file mode 100755 index 0000000..f807354 Binary files /dev/null and b/images/app_icons/app_icon-16x16.png differ diff --git a/images/app_icons/app_icon-180x180.png b/images/app_icons/app_icon-180x180.png new file mode 100755 index 0000000..38cd7b4 Binary files /dev/null and b/images/app_icons/app_icon-180x180.png differ diff --git a/images/app_icons/app_icon-192x192-mobile.png b/images/app_icons/app_icon-192x192-mobile.png new file mode 100755 index 0000000..e17e220 Binary files /dev/null and b/images/app_icons/app_icon-192x192-mobile.png differ diff --git a/images/app_icons/app_icon-192x192.png b/images/app_icons/app_icon-192x192.png new file mode 100755 index 0000000..cb2d203 Binary files /dev/null and b/images/app_icons/app_icon-192x192.png differ diff --git a/images/app_icons/app_icon-24x24.png b/images/app_icons/app_icon-24x24.png new file mode 100755 index 0000000..b98e992 Binary files /dev/null and b/images/app_icons/app_icon-24x24.png differ diff --git a/images/app_icons/app_icon-256x256-mobile.png b/images/app_icons/app_icon-256x256-mobile.png new file mode 100755 index 0000000..94d80ad Binary files /dev/null and b/images/app_icons/app_icon-256x256-mobile.png differ diff --git a/images/app_icons/app_icon-256x256.png b/images/app_icons/app_icon-256x256.png new file mode 100755 index 0000000..0b1546a Binary files /dev/null and b/images/app_icons/app_icon-256x256.png differ diff --git a/images/app_icons/app_icon-32x32.png b/images/app_icons/app_icon-32x32.png new file mode 100755 index 0000000..8e90f08 Binary files /dev/null and b/images/app_icons/app_icon-32x32.png differ diff --git a/images/app_icons/app_icon-384x384.png b/images/app_icons/app_icon-384x384.png new file mode 100755 index 0000000..a90eb66 Binary files /dev/null and b/images/app_icons/app_icon-384x384.png differ diff --git a/images/app_icons/app_icon-48x48.png b/images/app_icons/app_icon-48x48.png new file mode 100755 index 0000000..5cb9e11 Binary files /dev/null and b/images/app_icons/app_icon-48x48.png differ diff --git a/images/app_icons/app_icon-512x512.png b/images/app_icons/app_icon-512x512.png new file mode 100755 index 0000000..a7bf5c9 Binary files /dev/null and b/images/app_icons/app_icon-512x512.png differ diff --git a/images/app_icons/app_icon-64x64.png b/images/app_icons/app_icon-64x64.png new file mode 100755 index 0000000..87328f7 Binary files /dev/null and b/images/app_icons/app_icon-64x64.png differ diff --git a/images/app_icons/app_icon.svg b/images/app_icons/app_icon.svg new file mode 100755 index 0000000..80dc9ff --- /dev/null +++ b/images/app_icons/app_icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/bluetooth license.txt b/images/bluetooth license.txt new file mode 100755 index 0000000..f31bef9 --- /dev/null +++ b/images/bluetooth license.txt @@ -0,0 +1,34 @@ +Font Awesome Free License +------------------------- + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) +In the Font Awesome Free download, the CC BY 4.0 license applies to all icons +packaged as SVG and JS file types. + +# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +# Code: MIT License (https://opensource.org/licenses/MIT) +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +# Attribution +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +# Brand Icons +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/images/bluetooth.svg b/images/bluetooth.svg new file mode 100755 index 0000000..4444bb7 --- /dev/null +++ b/images/bluetooth.svg @@ -0,0 +1 @@ + diff --git a/images/circle.svg b/images/circle.svg new file mode 100755 index 0000000..6989354 --- /dev/null +++ b/images/circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/countries/Country Flags license.txt b/images/countries/Country Flags license.txt new file mode 100755 index 0000000..d7bc34d --- /dev/null +++ b/images/countries/Country Flags license.txt @@ -0,0 +1,63 @@ +License agreement + +Countryflags.com offers images of World flags and the U.S. States flags which are downloadable in various file formats. + +These downloadable formats are: + +*AI +*EPS +*SVG +*PDF +*PNG +*JPG + +Countryflags.com files are available for Commercial and Non-Commercial use. +Please read the following information about Commercial and Non-Commercial use below. + +Commercial use + +If you would like to use the country flags or the U.S. States flags for commercial use, purchase a license for one of the packages. +These professional packages are easy to use and will be downloadable after purchase. Save time by downloading a file type for all countries or by an entire nation at once. + +The intention of the packages for commercial purposes: + +*Websites; +*Development; +*App development; +*Web development; +*Product development; +*Printed products (for example mugs, flags, magnets, souvenirs, posters Etc.); +*Educational use without attribution; +*Papers and publications without attribution; + +Once you have purchased a license, you can use the downloads within this bundle for business and commercial purposes. +Please note that purchased downloads are temporarily available because of website/flag updates. +After buying a package, store your package safe and secure on your device or server. + +Not permitted + +*It is not allowed to sell or offer commercial packages as downloads; +*It is not allowed to add commercial packages to your portfolio; + +Non-commercial use + +All single files can be downloaded free of charge for private and for educational use. Besides, it is possible to view a file before purchasing a package for commercial use. + +The intention of the packages for non-commercial purposes such as: + +*Private use* +*Educational institutions** +*Theses** +*Newspapers** +*Publications** + +* Use all files free of charge and rights for private use; +** For educational purposes such as theses, papers and publications: always mention – both online and offline – the source Countryflags.com with your work. For online use, please use the following link: https://www.countryflags.com + +Not permitted + +*It is not allowed to sell or offer free files as downloads; +*It is not allowed to add free files to your portfolio; +*Multiplication on a large scale is not allowed with free downloadable files; + +If you have any questions about use and this License Agreement, please contact us at info@countryflags.com diff --git a/images/countries/america.svg b/images/countries/america.svg new file mode 100755 index 0000000..a4aad8a --- /dev/null +++ b/images/countries/america.svg @@ -0,0 +1 @@ + diff --git a/images/countries/china.svg b/images/countries/china.svg new file mode 100755 index 0000000..ade6500 --- /dev/null +++ b/images/countries/china.svg @@ -0,0 +1 @@ + diff --git a/images/countries/czech_republic.svg b/images/countries/czech_republic.svg new file mode 100755 index 0000000..c27f0d0 --- /dev/null +++ b/images/countries/czech_republic.svg @@ -0,0 +1 @@ + diff --git a/images/countries/germany.svg b/images/countries/germany.svg new file mode 100755 index 0000000..e60e2ab --- /dev/null +++ b/images/countries/germany.svg @@ -0,0 +1 @@ + diff --git a/images/countries/greece.svg b/images/countries/greece.svg new file mode 100755 index 0000000..e9ff7e2 --- /dev/null +++ b/images/countries/greece.svg @@ -0,0 +1 @@ + diff --git a/images/countries/netherlands.svg b/images/countries/netherlands.svg new file mode 100755 index 0000000..5af8d7c --- /dev/null +++ b/images/countries/netherlands.svg @@ -0,0 +1 @@ + diff --git a/images/down arrow license.txt b/images/down arrow license.txt new file mode 100755 index 0000000..f31bef9 --- /dev/null +++ b/images/down arrow license.txt @@ -0,0 +1,34 @@ +Font Awesome Free License +------------------------- + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) +In the Font Awesome Free download, the CC BY 4.0 license applies to all icons +packaged as SVG and JS file types. + +# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +# Code: MIT License (https://opensource.org/licenses/MIT) +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +# Attribution +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +# Brand Icons +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/images/down_arrow.svg b/images/down_arrow.svg new file mode 100755 index 0000000..4922d09 --- /dev/null +++ b/images/down_arrow.svg @@ -0,0 +1 @@ + diff --git a/images/ledger.svg b/images/ledger.svg new file mode 100755 index 0000000..3680668 --- /dev/null +++ b/images/ledger.svg @@ -0,0 +1 @@ + diff --git a/images/logo_big.svg b/images/logo_big.svg new file mode 100644 index 0000000..2a4b04d --- /dev/null +++ b/images/logo_big.svg @@ -0,0 +1,1605 @@ + + + + + + + + + diff --git a/images/logo_small.svg b/images/logo_small.svg new file mode 100644 index 0000000..2a4b04d --- /dev/null +++ b/images/logo_small.svg @@ -0,0 +1,1605 @@ + + + + + + + + + diff --git a/images/mask_images/mask_image.svg b/images/mask_images/mask_image.svg new file mode 100755 index 0000000..a590b69 --- /dev/null +++ b/images/mask_images/mask_image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/tile_images/tile_image-150x150.png b/images/tile_images/tile_image-150x150.png new file mode 100755 index 0000000..9d94470 Binary files /dev/null and b/images/tile_images/tile_image-150x150.png differ diff --git a/images/tile_images/tile_image-310x150.png b/images/tile_images/tile_image-310x150.png new file mode 100755 index 0000000..83b52d3 Binary files /dev/null and b/images/tile_images/tile_image-310x150.png differ diff --git a/images/tile_images/tile_image-310x310.png b/images/tile_images/tile_image-310x310.png new file mode 100755 index 0000000..e6e15b3 Binary files /dev/null and b/images/tile_images/tile_image-310x310.png differ diff --git a/images/tile_images/tile_image-70x70.png b/images/tile_images/tile_image-70x70.png new file mode 100755 index 0000000..4fae0e2 Binary files /dev/null and b/images/tile_images/tile_image-70x70.png differ diff --git a/images/touch_icons/touch_icon-114x114.png b/images/touch_icons/touch_icon-114x114.png new file mode 100755 index 0000000..9b02ad3 Binary files /dev/null and b/images/touch_icons/touch_icon-114x114.png differ diff --git a/images/touch_icons/touch_icon-120x120.png b/images/touch_icons/touch_icon-120x120.png new file mode 100755 index 0000000..0f87753 Binary files /dev/null and b/images/touch_icons/touch_icon-120x120.png differ diff --git a/images/touch_icons/touch_icon-144x144.png b/images/touch_icons/touch_icon-144x144.png new file mode 100755 index 0000000..e06f5ba Binary files /dev/null and b/images/touch_icons/touch_icon-144x144.png differ diff --git a/images/touch_icons/touch_icon-152x152.png b/images/touch_icons/touch_icon-152x152.png new file mode 100755 index 0000000..4fed82f Binary files /dev/null and b/images/touch_icons/touch_icon-152x152.png differ diff --git a/images/touch_icons/touch_icon-167x167.png b/images/touch_icons/touch_icon-167x167.png new file mode 100755 index 0000000..c231022 Binary files /dev/null and b/images/touch_icons/touch_icon-167x167.png differ diff --git a/images/touch_icons/touch_icon-180x180.png b/images/touch_icons/touch_icon-180x180.png new file mode 100755 index 0000000..b67ff20 Binary files /dev/null and b/images/touch_icons/touch_icon-180x180.png differ diff --git a/images/touch_icons/touch_icon-57x57.png b/images/touch_icons/touch_icon-57x57.png new file mode 100755 index 0000000..a170e57 Binary files /dev/null and b/images/touch_icons/touch_icon-57x57.png differ diff --git a/images/touch_icons/touch_icon-76x76.png b/images/touch_icons/touch_icon-76x76.png new file mode 100755 index 0000000..2f9efdb Binary files /dev/null and b/images/touch_icons/touch_icon-76x76.png differ diff --git a/images/trezor.svg b/images/trezor.svg new file mode 100755 index 0000000..338a475 --- /dev/null +++ b/images/trezor.svg @@ -0,0 +1 @@ + diff --git a/images/usb license.txt b/images/usb license.txt new file mode 100755 index 0000000..f31bef9 --- /dev/null +++ b/images/usb license.txt @@ -0,0 +1,34 @@ +Font Awesome Free License +------------------------- + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) +In the Font Awesome Free download, the CC BY 4.0 license applies to all icons +packaged as SVG and JS file types. + +# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +# Code: MIT License (https://opensource.org/licenses/MIT) +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +# Attribution +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +# Brand Icons +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/images/usb.svg b/images/usb.svg new file mode 100755 index 0000000..c087abd --- /dev/null +++ b/images/usb.svg @@ -0,0 +1 @@ + diff --git a/images/whale.svg b/images/whale.svg new file mode 100755 index 0000000..f3c0c33 --- /dev/null +++ b/images/whale.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100755 index 0000000..54d3fb7 --- /dev/null +++ b/index.html @@ -0,0 +1,2779 @@ + +"> + + + + + + $availableLanguage) { + + // Display language alternative page + echo ""; + + // Check if available language has an extension locale code that's not the same as its language identifier + if(array_key_exists("Constants", $availableLanguage) === TRUE && array_key_exists("Extension Locale Code", $availableLanguage["Constants"]) === TRUE && $availableLanguage["Constants"]["Extension Locale Code"] !== $languageIdentifier) { + + // Display language alternative page + echo ""; + } + } + ?> + + "> + "> + + + + + + + " onerror="processLoadingError(this, true);" type="image/svg+xml"> + " onerror="processLoadingError(this, true);" type="image/svg+xml"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous"> + " onerror="processLoadingError(this, true);" type="image/svg+xml"> + " onerror="processLoadingError(this, true);" type="image/svg+xml"> + + " integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);"> + " integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);"> + " integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);"> + + + + + + + + + + " crossorigin="use-credentials"> + "> + + + + + + + <?= encodeString(getTranslation('MWC Wallet')); ?> + + + + "> + + + + + + COPYRIGHT_YEAR) { + + // Display copyright information with the current year + echo ""; + echo ""; + } + + // Otherwise + else { + + // Display copyright information with the copyright year + echo ""; + echo ""; + } + + // Display theme color + echo ""; + + // Check if not crawler + if($isCrawler === FALSE) { + + // Go through all touch icons + foreach(TOUCH_ICONS as $touchIcon) { + + // Check if touch icon's parts aren't provided + if(count($touchIcon) !== count(TOUCH_ICON_PARTS)) + + // Display touch icon + echo ""; + + // Otherwise + else + + // Display touch icon + echo ""; + } + } + + // Go through all favicons + foreach(FAVICONS as $favicon) { + + // Check if favicon's parts aren't provided + if(count($favicon) !== count(FAVICON_PARTS)) + + // Display favicon + echo ""; + + // Otherwise + else + + // Display favicon + echo ""; + } + + // Go through all app icons + foreach(APP_ICONS as $appIcon) { + + // Check if app icon's parts aren't provided + if(count($appIcon) !== count(APP_ICON_PARTS)) + + // Display app icon + echo ""; + + // Otherwise check if app icon can be used as a favicon + else if($appIcon[APP_ICON_PARTS["Use As Favicon"]] === TRUE) + + // Display app icon + echo ""; + } + + // Go through all tile images + foreach(TILE_IMAGES as $tileImage) + + // Display tile image + echo ""; + ?> + + + +
$availableLanguage) { + + // Display available language button + echo ""; + } + ?>

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-sensitive data-dependencies='' data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-text="" title="">

" data-dependencies='' data-text="" title="">

" data-text="" title="">

" data-dependencies='' data-text="" title="">

    " . encodeString(getTranslation('N/A')) . ""; + } + + // Otherwise + else { + + // Go through all version changes + foreach(VERSION_CHANGES as $versionChange) { + + // Display version change + echo "
  • " . encodeString(getTranslation(escapeText($versionChange))) . "
  • "; + } + } + ?>

COPYRIGHT_YEAR) { + + // Display copyright information with the current year + echo "

" . encodeString(getTranslation('© %1$s–%2$s Nicolas Flamel.', [getNumberTranslation(COPYRIGHT_YEAR), getNumberTranslation($year)])) . "

"; + } + + // Otherwise + else { + + // Display copyright information with the copyright year + echo "

" . encodeString(getTranslation('© %1$s Nicolas Flamel.', [getNumberTranslation(COPYRIGHT_YEAR)])) . "

"; + } + ?>

    " . encodeString(getTranslation('N/A')) . ""; + } + + // Otherwise + else { + + // Go through all translation contributors + foreach(getTransactionContributors() as $link => $translationContributor) { + + // Check if contributor doesn't have a link + if(is_int($link) === TRUE) { + + // Display translation contributor + echo "
  • " . encodeString($translationContributor) . "
  • "; + } + + // Otherwise + else { + + // Display translation contributor + echo "
  • " . encodeString($translationContributor) . "
  • "; + } + } + } + ?>

    " . encodeString(getTranslation('N/A')) . ""; + } + ?>

" integrity="">

" onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous">" onerror="processLoadingError(this, true);" type="font/woff2" crossorigin="anonymous">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);">" integrity="" onerror="processLoadingError(this, true);" onload="processLoadingError(this);"> + diff --git a/languages/american_english.php b/languages/american_english.php new file mode 100755 index 0000000..7d8d6af --- /dev/null +++ b/languages/american_english.php @@ -0,0 +1,36 @@ + [ + + // Language + "Language" => "English", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/america.svg", + + // Currency + "Currency" => "USD", + + // Extension locale code + "Extension Locale Code" => "en", + + // Fallback + "Fallback" => "en" + ], + + // Text + "Text" => [ + '(?<=,) ' => ' ', + '(?<=.) ' => ' ', + '(?<=:) ' => ' ', + ',(?= )' => ',', + ] + ]; +?> diff --git a/languages/czech.php b/languages/czech.php new file mode 100755 index 0000000..8cc7602 --- /dev/null +++ b/languages/czech.php @@ -0,0 +1,734 @@ + [ + "blumchen" + ], + + // Constants + "Constants" => [ + + // Language + "Language" => "Čeština", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/czech_republic.svg", + + // Currency + "Currency" => "CZK", + + // Extension locale code + "Extension Locale Code" => "cs", + + // Fallback + "Fallback" => "cs" + ], + + // Text + "Text" => [ + '(?<=,) ' => ' ', + '(?<=.) ' => ' ', + '(?<=:) ' => ' ', + ',(?= )' => ',', + ' (%1$c)' => ' (%1$c)', + '%1$d at %2$t' => '%1$d v %2$t', + '#%1$s' => '#%1$s', + '%1$s%%' => '%1$s%%', + '%1$s–%2$s' => '%1$s–%2$s', + '© %1$s–%2$s Nicolas Flamel.' => '© %1$s–%2$s Nicolas Flamel.', + '© %1$s Nicolas Flamel.' => '© %1$s Nicolas Flamel.', + '%1$u:' => '%1$u:', + '%1$x/%2$x/v%3$v' => '%1$x/%2$x/v%3$v', + '%1$y: %2$m' => '%1$y: %2$m', + '%1$y: %2$m, License: %3$m' => '%1$y: %2$m, Licence: %3$m', + 'About' => 'O peněžence', + 'About Error' => 'O chybě', + 'Access to your camera was denied.' => 'Přístup k vaší kameře byl odepřen.', + 'Account' => 'Účet', + 'Account Error' => 'Chyba účtu', + 'Address' => 'Adresa', + 'Address:' => 'Adresa:', + 'Address Copied' => 'Adresa zkopírována', + 'A listener address hasn\'t been provided.' => 'Nebyla poskytnuta listener adresa.', + 'All' => 'Vše', + 'All Error' => 'Všechny chyby', + 'Allow changing base fee' => 'Povolit úpravy základního poplatku', + 'Allows changing a transaction\'s base fee when sending a payment' => 'Povolí úpravy základního poplatku transakce při odesílání platby', + 'Allows processing mining related API requests' => 'Povolí zpracování požadavků API souvisejících s těžbou', + 'A MimbleWimble Coin wallet.' => 'MimbleWimble Coin peněženka.', + 'Amount' => 'Částka', + 'Amount:' => 'Částka:', + 'Amount: %1$c' => 'Částka: %1$c', + 'Amount is empty.' => 'Částka je prázdná.', + 'Amount is invalid.' => 'Částka je neplatná.', + /*TODO*///'Amount\'s value when recorded:' => '', + 'An error has occurred. This app will automatically reload in a few seconds.' => 'Došlo k chybě. Aplikace se za pár sekund znovu načte.', + 'An error has occurred. This site will automatically reload in a few seconds.' => 'Došlo k chybě. Stránka se za pár sekund znovu načte.', + 'An error occurred while trying to access your camera.' => 'Při pokes o přístup k vaší kameře došlo k chybě.', + /*TODO*///'An internal error occurred.' => '', + 'A node address hasn\'t been provided.' => 'Nebyla zadána adresa uzlu.', + 'An operation is currently being performed on the wallet\'s address suffix.' => 'Právě probíhá operace s příponou adresy peněženky.', + 'An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.' => 'Pro tuto aplikaci je k dispozici aktualizace. Chcete aktualizaci nainstalovat nyní? Pokud ano, tato aplikace se po instalaci aktualizace znovu načte. Pokud ne, aktualizace se nainstaluje při příštím otevření této aplikace.', + 'An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.' => 'Aktualizace pro tuto stránku je k dispozici. Chcete aktualizaci nainstalovat nyní? Pokud ano, tato stránka se po instalaci aktualizace znovu načte. Pokud ne, aktualizace se nainstaluje při příští návštěvě této stránky.', + 'API Settings' => 'Nastavení API', + 'Approve exporting the root public key for the account at index %1$s on that hardware wallet.' => 'Schvalte export kořenového veřejného klíče pro účet s indexem %1$s na vaší hardware peněžence.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue mining.' => 'Pro pokračování v těžbě schvalte přijetí transakce pro %1$y na vaší hardware peněžence.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.' => 'Pro pokračování v přijímání transakce schvalte přijetí transakce pro %1$y na vaší hardware peněžence.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.' => 'Pro pokračování v těžbě schvalte přijetí transakce pro Peněženka %1$s na vaší hardware peněžence.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Pro pokračování v přijímání transakce schvalte přijetí transakce pro Peněženka %1$s na vaší hardware peněžence.', + /*TODO*///'Approve Receiving Payment' => '', + 'Approve sending the transaction on the hardware wallet for %1$y to continue sending the payment.' => 'Pro pokračování v odesílání dané transakce z %1$y schvalte odesílání transakce na vaší hardware peněžence.', + 'Approve sending the transaction on the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Pro pokračování v odesílání dané transakce z Peněženka %1$s schvalte odesílání transakce na vaší hardware peněžence.', + 'Approving the transaction on the hardware wallet was denied.' => 'Schválení transakce na hardware peněžence bylo zamítnuto.', + 'Are you sure you want to cancel transaction %1$s?' => 'Opravdu chcete zrušit transakci %1$s?', + 'Are you sure you want to change the address suffix for %1$y?' => 'Opravdu chcete změnit příponu adresy pro %1$y?', + 'Are you sure you want to change the address suffix for Wallet %1$s?' => 'Opravdu chcete změnit příponu adresy pro Peněženka %1$s?', + 'Are you sure you want to delete %1$y?' => 'Opravdu chcete smazat %1$y?', + 'Are you sure you want to delete all your wallets?' => 'Opravdu chcete smazat všechny vaše peněženky?', + 'Are you sure you want to delete Wallet %1$s?' => 'Opravdu chcete smazat Peněženka %1$s?', + 'Are you sure you want to exit? There\'s a remaining transaction.' => 'Opravdu chcete odejít? Je zde zbývající transakce.', + 'Are you sure you want to exit? There\'s remaining transactions.' => 'Opravdu chcete odejít? Jsou zde zbývající transakce.', + 'Are you sure you want to reset the settings to their default values?' => 'Opravdu chcete obnovit nastavení na výchozí hodnoty?', + 'Are you sure you want to resync %1$y?' => 'Opravdu chcete znovu synchronizovat %1$y?', + 'Are you sure you want to resync Wallet %1$s?' => 'Opravdu chcete znovu synchronizovat Peněženka %1$s?', + 'Attributions' => 'Atribuce', + /*TODO*///'Automatically approve receiving payments' => '', + /*TODO*///'Automatically approves all received payments without requiring manual approval' => '', + 'Automatically lock after a set duration of inactivity' => 'Automaticky uzamknout po nastavené době nečinnosti', + 'Automatically lock when not focused' => 'Automaticky uzamknout pokud běží na pozadí', + 'Automatic lock activated.' => 'Automatický zámek aktivován.', + 'A wallet can\'t send payments to itself.' => 'Peněženka nemůže odesílat platby sama sobě.', + 'A wallet with the same passphrase already exists in your list of wallets.' => 'Peněženka se stejnou bezpečnostní frází již ve vašem seznamu peněženek existuje.', + 'A wallet with the same root public key already exists in your list of wallets.' => 'Peněženka se stejným kořenovým veřejným klíčem již ve vašem seznamu peněženek existuje.', + 'Back' => 'Zpět', + 'Bad Gateway' => 'Chyba Bad Gateway', + 'Balance' => 'Zůstatek', + 'Base fee' => 'Základní poplatek', + 'Base fee is empty.' => 'Základní poplatek je prázdný.', + 'Base fee is invalid.' => 'Základní poplatek je neplatný.', + /*TODO*///'Bitcoin:' => '', + 'Broadcasting the transaction failed.' => 'Vysílání transakce se nezdařilo.', + 'Broadcasting the transaction failed for the following reason.' => 'Vysílání transakce se nezdařilo z následujícího důvodu.', + 'Broadcast transactions can\'t be canceled.' => 'Vyslané transakce nemohou být zrušeny.', + 'Cancel' => 'Zrušit', + 'Canceled' => 'Zrušeno', + 'Canceled transactions can\'t be rebroadcast.' => 'Zrušené transakce nemohou být znovuvyslány.', + /*TODO*///'Cancel Error' => '', + 'Change Address Suffix' => 'Změnit příponu adresy', + 'Change Address Suffix Error' => 'Chyba změny přípony adresy', + 'Changed %1$y setting.' => 'Nastavení %1$y změněno.', + 'Changed %1$y setting to %2$y.' => 'Nastavení %1$y změněno na %2$y.', + 'Changed password.' => 'Heslo změněno.', + 'Change Password' => 'Změnit heslo', + 'Change Password Error' => 'Chyba změny hesla', + 'Changing the address suffix failed.' => 'Změna přípony addresy se nezdařila.', + 'Changing the address suffix timed out.' => 'Vypršel časový limit žádosti pro změnu přípony adresy.', + 'Changing your password failed.' => 'Změna vašeho hesla se nezdařila.', + 'coinbase' => 'coinbase', + 'Coinbase' => 'Coinbase', + 'Coinbase transactions can\'t be rebroadcast.' => 'Coinbase transakce nemohou být znovuvyslány.', + /*TODO*///'Coinbase transactions don\'t have a file response.' => '', + 'Compatibility Error' => 'Chyba kompatibility', + 'Confirmed' => 'Potvrzeno', + 'Confirmed amount:' => 'Potvrzená částka:', + 'Confirmed and unconfirmed' => 'Potvrzeno a nepotvrzeno', + 'Confirmed height:' => 'Výška bloku potvrzení:', + 'Confirmed transactions can\'t be canceled.' => 'Potvrzené transakce nemohou být zrušeny.', + 'Confirmed transactions can\'t be rebroadcast.' => 'Potvrzené transakce nemohou být znovuvyslány.', + 'Confirm new password' => 'Potvrdit nové heslo', + 'Confirm new password is empty.' => 'Potvrdit nové heslo je prázdné.', + 'Confirm Password' => 'Potvrdit heslo', + 'Confirm password is empty.' => 'Potvrdit heslo je prázdné.', + 'Confirm Payment Details' => 'Potvrdit detaily platby', + 'Connect' => 'Připojit', + 'Connecting to a hardware wallet.' => 'Připojuji se k hardware peněžence.', + 'Connecting to a node failed.' => 'Připojení k uzlu se nezdařilo.', + 'Connecting to that hardware wallet failed.' => 'Připojení k dané hardware peněžence se nezdařilo.', + /*TODO*///'Connecting to the host failed.' => '', + 'Connecting to the listener failed.' => 'Připojení k danému listeneru se nezdařilo.', + 'Connecting to the node failed.' => 'Připojení k danému uzlu se nezdařilo.', + 'Connecting to the prices server failed.' => 'Připojení k cenové serveru se nezdařilo.', + 'Connecting to the recipient failed.' => 'Připojení k příjemci se nezdařilo.', + 'Connect the hardware wallet for %1$y to continue getting the payment proof address.' => 'Připojte hardware peněženku, aby %1$y mohla pokračovat v získávání adresy potvrzení o platbě.', + 'Connect the hardware wallet for %1$y to continue mining.' => 'Připojte hardware peněženku, aby %1$y mohla pokračovat v těžbě.', + 'Connect the hardware wallet for %1$y to continue receiving a payment.' => 'Připojte hardware peněženku, aby %1$y mohla pokračovat v přijímání platby.', + 'Connect the hardware wallet for %1$y to continue sending the payment.' => 'Připojte hardware peněženku, aby %1$y mohla pokračovat v odesílání platby.', + 'Connect the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Připojte hardware peněženku, aby Peněženka %1$s mohla pokračovat v získávání adresy potvrzení o platbě.', + 'Connect the hardware wallet for Wallet %1$s to continue mining.' => 'Připojte hardware peněženku, aby Peněženka %1$s mohla pokračovat v těžbě.', + 'Connect the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Připojte hardware peněženku, aby Peněženka %1$s mohla pokračovat v přijímání platby.', + 'Connect the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Připojte hardware peněženku, aby Peněženka %1$s mohla pokračovat v odesílání platby.', + 'Continue' => 'Pokračovat', + 'Copy' => 'Kopírovat', + 'Copy Address Error' => 'Chyba zkopírování adresy', + 'Copy ID Error' => 'Chyba zkopírování ID', + 'Copying the address to your clipboard failed.' => 'Kopírování dané adresy do vaší schránky se nezdařilo.', + 'Copying the ID to your clipboard failed.' => 'Kopírování daného ID do vaší schránky se nezdařilo.', + 'Copying the value to your clipboard failed.' => 'Kopírování dané hodnoty do vaší schránky se nezdařilo.', + 'Copyright' => 'Copyright', + 'Copy Value Error' => 'Chyba kopírování hodnoty', + 'Create' => 'Vytvořit', + 'Created hardware wallet Wallet %1$s.' => 'Vytvořena hardware peněženka Peněženka %1$s.', + 'Created wallet Wallet %1$s.' => 'Vytvořena peněženka Peněženka %1$s.', + 'Create Error' => 'Chyba vytváření', + 'Create Wallet Error' => 'Chyba vytváření peněženky', + 'Creating slate failed.' => 'Slate vytváření se nezdařilo.', + 'Creating transaction failed.' => 'Vytváření transakce se nezdařilo.', + 'Creating wallet failed.' => 'Vytváření peněženky se nezdařilo.', + 'Currency of the displayed prices' => 'Měna zobrazovaných cen', + 'Current password' => 'Současné heslo', + 'Current password is empty.' => 'Současné heslo je prázdné.', + 'Current password is incorrect.' => 'Současné heslo je nesprávné.', + 'Custom listener address' => 'Adresa vlastního listeneru', + 'Custom node address' => 'Adresa vlastního uzlu', + 'Custom node foreign API secret' => 'Tajemství cizího API vlastního uzlu', + 'Custom Tor proxy address' => 'Vlastní Tor proxy adresa', + 'Default Base Fee' => 'Defaultní základní poplatek', + 'Delete' => 'Smazat', + 'Delete All Wallets' => 'Smazat všechny peněženky', + 'Delete All Wallets Error' => 'Smazat všechny peněženky - chyba', + 'Deleted all wallets.' => 'Všechny peněženky smazány.', + 'Deleted wallet %1$y.' => 'Smazána peněženka %1$y.', + 'Deleted wallet Wallet %1$s.' => 'Smazána peněženka Peněženka %1$s.', + 'Delete Error' => 'Chyba smazání', + /*TODO*///'Description' => '', + 'Destination:' => 'Destinace:', + 'Disclaimer' => 'Vyloučení odpovědnosti', + 'Disconnected from the listener.' => 'Odpojeno od listeneru.', + 'Disconnected from the node.' => 'Odpojeno od uzlu.', + 'Displayed address type' => 'Typ zobrazované adresy', + 'Displayed amount type' => 'Typ zobrazované částky', + 'Display prices' => 'Zobrazit ceny', + 'Displays a message when not able to connect to a listener or when disconnected from a listener' => 'Zobrazí zprávu v případě neúspěšného připojení k listeneru, případně pokud dojde k odpojení', + 'Displays a message when not able to connect to a node, when a connected node is incompatible, or when disconnected from a node' => 'Zobrazí zprávu v případě neúspěšného připojení k uzlu, pokud je připojený uzel nekompatibilní, případně pokud dojde k odpojení', + 'Displays prices for amounts' => 'Zobrazí ceny pro dané částky', + /*TODO*///'Donate' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this app.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this extension.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this site.' => '', + 'Don\'t disclose this passphrase to anyone.' => 'Tuto bezpečnostní frázi s nikým nesdílejte.', + 'Down' => 'Dolů', + /*TODO*///'Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*///'Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*/'Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Každá peněženka může být obnovena bezpečnostní frází pouze po té, co byla daná peněženky smazána.', + 'Enable mining API' => 'Povolit API těžbu', + 'Enter a new name for %1$y.' => 'Zadejte nový název pro %1$y.', + 'Enter a new name for Wallet %1$s.' => 'Zadejte nový název pro Peněženka %1$s.', + 'Enter a wallet\'s passphrase to recover it.' => 'Pro obnovení peněženky zadejte její bezpečnostní frázi.', + 'Enter your password to continue sending the payment.' => 'Pro pokračování v odesílání platby zadejte heslo.', + 'Enter your password to get the passphrase for %1$y.' => 'K zobrazení bezpečnostní fráze pro %1$y zadejte heslo.', + 'Enter your password to get the passphrase for Wallet %1$s.' => 'K zobrazení bezpečnostní fráze pro Peněženka %1$s zadejte heslo.', + /*TODO*///'Enter your pin as the following alphabetic characters to unlock the hardware wallet.' => '', + /*TODO*///'Epic Cash' => '', + /*TODO*///'Epic Cash:' => '', + 'Error' => 'Chyba', + 'Error %1$s' => 'Chyba %1$s', + 'Error response from the recipient.' => 'Chybová odpověď od příjemce.', + 'Expired' => 'Vypršeno', + 'Expired transactions can\'t be canceled.' => 'Vypršené transakce nemohou být zrušeny.', + 'Expired transactions can\'t be rebroadcast.' => 'Vypršené transakce nemohou být znovuvyslány.', + 'Expire height:' => 'Výška expirace:', + 'Exporting the root public key on that %1$x hardware wallet was denied.' => 'Export kořenového veřejného klíče na dané hardware peněžence %1$x bylo zamítnuto.', + 'Failed to determine the primary instance.' => 'Nepodařilo se určit primární instanci.', + 'Failed to initialize dependencies.' => 'Nepodařilo se inicializovat závislosti.', + 'Failed to install or update the service worker.' => 'Service worker se nepodařilo nainstalovat nebo aktualizovat.', + 'Failed to load resources. Refresh this site to try again.' => 'Nepodařilo se načíst zdroje. Obnovte tuto stránku a zkuste to znovu.', + 'Failed to load resources. Restart this app to try again.' => 'Nepodařilo se načíst zdroje. Obnovte tuto aplikaci a zkuste to znovu.', + 'Fee:' => 'Poplatek:', + /*TODO*///'Fee\'s value when recorded:' => '', + 'Finalize' => 'Finalizovat', + 'Finalize the transaction for %1$y to continue sending the payment.' => 'Pro pokračování v odesílání dané transakce z %1$y transakci finalizujte.', + 'Finalize the transaction for Wallet %1$s to continue sending the payment.' => 'Pro pokračování v odesílání dané transakce z Peněženka %1$s transakci finalizujte.', + 'Finalizing the slate failed.' => 'Slate finalizace se nezdařila.', + 'First' => 'První', + 'Floonet' => 'Floonet', + 'Floonet/testnet' => 'Floonet/testnet', + 'Forbidden' => 'Zakázáno', + 'Forbidden response from the recipient.' => 'Zakázaná odpověď od příjemce.', + 'Forward' => 'Dopředu', + 'From wallet' => 'Z peněženky', + 'Gateway Timeout' => 'Gateway Timeout Error', + /*TODO*///'Get File Response' => '', + /*TODO*///'Get File Response Error' => '', + 'Get Passphrase' => 'Zobrazit bezpečnostní frázi', + 'Get Passphrase Error' => 'Chyba zobrazení bezpečnostní fráze', + 'Get Payment Proof Address' => 'Zobrazit adresu potvrzení o platbě', + 'Get Payment Proof Address Error' => 'Chyba zobrazení adresy potvrzení o platbě', + 'Getting the app information from that %1$x hardware wallet failed.' => 'Získávání informací o aplikaci z dané hardware peněženky %1$x se nezdařilo.', + /*TODO*///'Getting the features from that %1$x hardware wallet failed.' => '', + 'Getting the payment proof address failed.' => 'Získání adresy potvrzení o platbě se nezdařilo.', + 'Getting the root public key from that %1$x hardware wallet failed.' => 'Získání kořenového veřejného klíče z dané hardware peněženky %1$x se nezdařilo.', + 'Getting the seed cookie from that %1$x hardware wallet failed.' => 'Získání seed cookie z dané hardware peněženky %1$x se nezdařilo.', + /*TODO*///'Give the %1$m file to the payment\'s recipient and open their response file to continue sending the payment.' => '', + /*TODO*///'Give the %1$m file to the payment\'s sender for them to finalize the transaction.' => '', + 'Grin' => 'Grin', + 'Grin:' => 'Grin:', + 'Hardware' => 'Hardware', + 'Hardware wallet' => 'Hardware peněženka', + 'Hardware Wallet' => 'Hardware Peněženka', + 'Hardware Wallet Approval Requested' => 'Vyžadováno schválení hardware peněženky', + 'Hardware wallet connected' => 'Hardware peněženka připojena', + 'Hardware Wallet Disconnected' => 'Hardware peněženka odpojena', + 'Hardware Wallet Error' => 'Chyba hardware peněženky', + 'Hardware Wallet Locked' => 'Hardware peněženka uzamčena', + 'Hardware wallet support' => 'Hardware peněženka - podpora', + 'height locked' => 'uzamčená výška', + 'Hide' => 'Skrýt', + 'HTTP' => 'HTTP', + 'HTTPS' => 'HTTPS', + 'ID:' => 'ID:', + 'ID Copied' => 'ID zkopírováno', + /*TODO*///'If someone asked you to copy/paste something here you are being scammed!!!' => '', + /*TODO*///'If you paid to access MWC Wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back' => '', + 'Inactive lock activated.' => 'Zámek neaktivity aktivován.', + 'Incorrect password.' => 'Nesprávné heslo.', + /*TODO*///'Incorrect pin.' => '', + /*TODO*///'Information' => '', + 'Install Now' => 'Nainstalovat nyní', + 'Install the MWC Wallet app?' => 'Nainstalovat aplikaci MWC Wallet?', + 'Insufficient balance.' => 'Nedostatečný zůstatek.', + 'Interface Settings' => 'Nastavení rozhraní', + 'Internal error' => 'Interní chyba', + 'Internal Server Error' => 'Chyba interního serveru', + 'Invalid amount.' => 'Neplatná částka.', + 'Invalid message.' => 'Neplatná zpráva.', + /*TODO*///'Invalid message from the host.' => '', + 'Invalid name.' => 'Neplatný název.', + 'Invalid origin.' => 'Neplatný původ.', + 'Invalid parameters' => 'Neplatné parametry', + 'Invalid passphrase.' => 'Neplatná bezpečnostní fráze.', + /*TODO*///'Invalid pin.' => '', + 'Invalid recipient address.' => 'Neplatná adresa příjemce.', + 'Invalid request' => 'Neplatný požadavek', + 'Invalid request.' => 'Neplatný požadavek.', + 'Invalid request response from the recipient.' => 'Odpověď "neplatný požadavek" od příjemce.', + 'Invalid response from the recipient.' => 'Neplatná odpověď příjemce.', + 'Invalid wallet.' => 'Neplatná peněženka.', + 'JavaScript Error' => 'JavaScript chyba', + 'Kernel excess:' => 'Kernel přebytek:', + 'Language' => 'Jazyk', + 'Language changed to %1$y.' => 'Jazyk změněn na %1$y.', + 'Language\'s currency' => 'Měna jazyka', + 'Last' => 'Poslední', + 'Ledger Flex' => 'Ledger Flex', + 'Ledger Nano S' => 'Ledger Nano S', + 'Ledger Nano S Plus' => 'Ledger Nano S Plus', + 'Ledger Nano X' => 'Ledger Nano X', + 'Ledger Stax' => 'Ledger Stax', + 'Listener' => 'Listener', + 'Listener connected' => 'Listener připojen', + 'Listener disconnected' => 'Listener odpojen', + 'Listener Error' => 'Chyba listeneru', + 'Listener Settings' => 'Nastavení listeneru', + 'Listener settings changed. Disconnecting from the listener.' => 'Nastavení listeneru změněno. Odpojuji se od listeneru.', + 'Loading…' => 'Načítá se…', + 'Loading Error' => 'Chyba načítání', + 'Lock' => 'Uzamknout', + 'Locked.' => 'Uzamčeno.', + 'Lock height:' => 'Uzamčít výšku:', + 'Lock if not focused' => 'Uzamknout pokud není aktivně používáno', + 'Log' => 'Protokol', + 'Log Error' => 'Protokol - chyba', + 'Log initialized.' => 'Protokol zahájen.', + /*TODO*///'Login With Wallet' => '', + 'Mainnet' => 'Mainnet', + 'Maintenance' => 'Údržba', + 'Make sure that the correct app is open on the hardware wallet.' => 'Ujistěte se, že máte na vaší hardware peněžence otevřenou správnou aplikaci.', + 'Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.' => 'Ujistěte se, že máte na vaší hardware peněžence otevřenou správnou aplikaci a že daná hardware peněženka není uzamčena.', + /*TODO*///'Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m' => '', + /*TODO*///'Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m' => '', + /*TODO*///'Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m' => '', + /*TODO*///'Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m' => '', + 'Manage your MimbleWimble Coin' => 'Spravujte váš MimbleWimble Coin', + 'Match connection' => 'Ταίριασμα σύνδεσης', + 'Maximum number of messages that the log can display' => 'Maximální počet zpráv, které může protokol zobrazit', + 'Message:' => 'Zpráva:', + 'Method not found' => 'Metoda nenalezena', + 'MimbleWimble Coin' => 'MimbleWimble Coin', + 'MimbleWimble Coin:' => 'MimbleWimble Coin:', + 'Minutes between price updates' => 'Počet minut mezi aktualizacemi cen', + 'Minutes of inactivity required for automatic lock' => 'Minuty neaktivity potřebné k aktivaci automatického zámku', + 'MWC Wallet' => 'MWC Wallet', + 'MWC Wallet — Error' => 'MWC Wallet — chyba', + 'MWC Wallet — Error %1$s' => 'MWC Wallet — chyba %1$s', + 'MWC Wallet is an extension that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC Wallet je rozšíření, které vám umožňuje spravovat váš MimbleWimble Coin ve vašem prohlížeči.', + /*TODO*///'MWC Wallet is an open-source, self-custodial wallet that allows you to easily send and receive MimbleWimble Coin. Designed with ease of use and accessibility in mind, this wallet runs on everything from smartwatches to mainframes while providing an intuitive interface that makes using it a breeze.' => '', + 'MWC Wallet is a self-custodial web wallet that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC Wallet je "self-custody" webová peněženka, která vám umožňuje spravovat váš MimbleWimble Coin ve vašem prohlížeči.', + 'MWC Wallet — Maintenance' => 'MWC Wallet — údržba', + 'N/A' => 'N/A', + 'Name' => 'Jméno', + 'Network changed. Disconnecting from the listener.' => 'Síť změněna. Odpojuji se od listeneru.', + 'Network type' => 'Druh sítě', + 'Network type:' => 'Druh sítě:', + 'New password' => 'Nové heslo', + 'New password is empty.' => 'Nové heslo je prázdné.', + 'New passwords don\'t match.' => 'Nova hesla se neshodují.', + 'New Wallet Passphrase' => 'Nová Peněženka - bezpečnostní fráze', + 'Next' => 'Další', + 'Nicolas Flamel' => 'Nicolas Flamel', + 'No' => 'Ne', + 'No camera was found. Connect a camera to use this feature.' => 'Kamera nebyla nalezena. Připojte kameru, abyste mohli využívat tuto funkci.', + 'Node' => 'Uzel', + 'Node connected' => 'Uzel připojen', + 'Node disconnected' => 'Uzel odpojen', + 'Node Error' => 'Chyba uzlu', + 'Node Settings' => 'Nastavení uzlu', + 'Node settings changed. Disconnecting from the node.' => 'Nastavení uzlu změněno. Odpojuji se od uzlu.', + 'Node warning' => 'Uzel - varování', + 'No hardware wallet was selected.' => 'Nebyla zvolena žádná hardware peněženka.', + 'Non-hardware wallet' => 'Jiná než hardware peněženka', + 'no recent duplicate' => 'žádný nedávný duplikát', + 'Not compatible with node versions less than %1$v.' => 'Není kompatibilní s uzly verze nižší než %1$v.', + 'Not Found' => 'Nenalezeno', + 'Not found response from the recipient.' => 'Odpověď příjemce - "Nenalezeno".', + 'No transactions exist for this wallet.' => 'Pro tuto peněženku neexistují žádné transakce.', + 'Number of confirmations:' => 'Počet potvrzení:', + 'Number of confirmations required to spend an output' => 'Počet potvrzení potřebných k utracení výstupu transakce', + 'OK' => 'OK', + 'Onion Service' => 'Onion Service', + 'Only one instance of this app can be open at once. Close all other instances to continue.' => 'Naráz může být otevřena pouze jedna instance této aplikace. Pro pokračování zavřete všechny ostatní instance.', + 'Only one instance of this extension can be open at once. Close all other instances to continue.' => 'Naráz může být otevřena pouze jedna instance tohoto rozšíření. Pro pokračování zavřete všechny ostatní instance.', + 'Only one instance of this site can be open at once. Close all other instances to continue.' => 'Naráz může být otevřena pouze jedna instance této stránky. Pro pokračování zavřete všechny ostatní instance.', + /*TODO*///'Opening that file failed.' => '', + 'Open in New Tab' => 'Otevřít v nové záložce', + 'Open in New Window' => 'Otevřít v novém okně', + /*TODO*///'Open Response File' => '', + 'Optional message' => 'Volitelná zpráva', + 'Order Error' => 'Order Error', + 'Output commitment:' => 'Závazek výstupu:', + 'Passphrase' => 'Bezpečnostní fráze', + 'Passphrases can\'t be retrieved from hardware wallets.' => 'Bezpečnostní fráze nemohou být získány z hardware peněženek.', + 'Password' => 'Heslo', + 'Password Changed' => 'Heslo změněno', + 'Password is empty.' => 'Heslo je prázdné.', + 'Passwords don\'t match.' => 'Hesla se neshodují.', + 'Payload too large response from the recipient.' => 'Odpověď příjemce: "příliš velká zátěž".', + 'Payment Details' => 'Detaily platby', + 'Payment Proof' => 'Potvrzení o platbě', + 'Payment Proof Address' => 'Adresa potvrzení o platbě', + 'Payment Received' => 'Platba přijata', + /*TODO*///'Pin' => '', + 'plain' => 'plain', + 'Previous' => 'Předchozí', + 'Private Browsing And Site Data Information' => 'Soukromé prohlížení a informace o údajích webu', + /*TODO*///'Rebroadcast' => '', + /*TODO*///'Rebroadcast Error' => '', + 'Rebroadcasting the transaction failed.' => 'Znovuvyslání dané transakce se nezdařilo.', + 'Rebroadcasting the transaction failed for the following reason.' => 'Znovuvyslání dané transakce se nezdařilo z následujícího důvodu.', + 'Received' => 'Přijato', + 'Received an invalid response from the node.' => 'Obdržena neplatná odpověď uzlu.', + 'Received an invalid response from the prices server.' => 'Obdržena neplatná odpověď cenového serveru.', + 'Received transactions can\'t be rebroadcast.' => 'Přijaté transakce nemohou být znovuvyslány.', + /*TODO*///'Receive Payment As File' => '', + /*TODO*///'Receive Payment As File Error' => '', + 'Recipient address' => 'Adresa příjemce', + 'Recipient address is empty.' => 'Adresa příjemce je prázdná.', + 'Recipient address is invalid.' => 'Adresa příjemce je neplatná.', + 'Recipient address isn\'t supported.' => 'Adresa příjemce není podporována.', + 'Recipient doesn\'t support any available slate versions.' => 'Příjemce nepodporuje žádné dostupné slate verze.', + 'Recipient doesn\'t support payment proofs.' => 'Příjemce nepodporuje potvrzení o platbě.', + 'Recipient payment proof address:' => 'Adresa příjemce pro potvrzení o platbě:', + 'Recipient payment proof signature:' => 'Podpis příjemce pro potvrzení o platbě:', + 'Recipient\'s foreign API version isn\'t supported.' => 'Cizí API verze příjemce není podporována.', + 'Recipient\'s slate versions aren\'t supported.' => 'Slate verze příjemce nejsou podporovány.', + 'Recorded time:' => 'Zaznamenaný čas:', + 'Recover' => 'Obnovit', + 'Recovered wallet Wallet %1$s.' => 'Obnovená peněženka Peněženka %1$s.', + 'Recover Wallet' => 'Obnovit Peněženku', + 'Recover Wallet Error' => 'Chyba obnovení peněženky', + 'Refresh this site to try again.' => 'Obnovte tuto stránku a zkuste to znovu.', + 'Relative height is invalid.' => 'Relativní výška je neplatná.', + 'Release date:' => 'Datum vydání:', + /*TODO*///'Reload Required' => '', + 'Remind me later' => 'Připomeň mi později', + 'Rename' => 'Přejmenovat', + 'Renamed wallet %1$y to %2$y.' => 'Přejmenována peněženka %1$y na %2$y.', + 'Renamed wallet Wallet %1$s to %2$y.' => 'Přejmenována peněženka Peněženka %1$s na %2$y.', + 'Rename Error' => 'Chyba přejmenování', + 'Required number of confirmations:' => 'Požadovaný počet potvrzení:', + 'Require payment proof' => 'Požadovat potvrzení o platbě', + 'Requires all new transactions to have a payment proof' => 'Požaduje, aby všechny nové transakce obsahovaly potvrzení o platbě', + 'Reset settings.' => 'Resetovat nastavení.', + 'Reset Settings' => 'Resetovat Nastavení', + 'Reset Settings Error' => 'Chyba Resetovaní Nastavení', + 'Restart this app to try again.' => 'Restartujte tuto aplikaci a zkuste to znovu.', + 'Restart this extension to try again.' => 'Restartujte toto rozšíření a zkuste to znovu.', + 'Resync' => 'Opětovná synchronizace', + 'Resync Error' => 'Chyba opětovné synchronizace', + 'Resyncing can also use a lot of data depending on the number of unspent transaction outputs present in the blockchain.' => 'Opětovná synchronizace může, v závislosti na počtu neutracených výstupů transakce přítomných v blockchainu, vyžadovat hodně dat.', + 'Resyncing can take a significant amount of time to complete, and you won\'t be able to send payments from this wallet until it\'s finished resyncing.' => 'Opětovná synchronizace může zabrat značnou porci času, a během ní nebude možné z této peněženky odesílat platby.', + 'Scan QR Code' => 'Naskenovat QR kód', + 'Scan QR Code Error' => 'Chyba skenování QR kódu', + 'Selected' => 'Zvoleno', + 'Select passphrase\'s origin.' => 'Zvolte původ bezpečnostní fráze.', + 'Send' => 'Odeslat', + /*TODO*///'Send as file' => '', + 'Sender payment proof address:' => 'Adresa odesílatele pro potvrzení o platbě:', + 'Send Error' => 'Chyba odeslání', + /*TODO*///'Sending %1$c from %2$y for a fee of %3$c.' => '', + 'Sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Odesílám %1$c z %2$y na následující adresu za poplatek %3$c.', + /*TODO*///'Sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'Sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Odesílám %1$c z Peněženka %2$s na následující adresu za poplatek %3$c.', + 'Sending Payment' => 'Odesílání Platby', + 'Send Payment' => 'Odeslat Platbu', + 'Send Payment Error' => 'Chyba při odesílání platby', + 'Sent' => 'Odesláno', + /*TODO*///'Sent transactions don\'t have a file response.' => '', + /*TODO*///'Set Payment Proof Address Index' => '', + 'Sets the address for the custom listener' => 'Nastaví adresu vašeho vlastního listeneru', + 'Sets the address for the custom node' => 'Nastaví adresu vašeho vlastního uzlu', + 'Sets the address for the custom Tor proxy' => 'Nastaví adresu vašeho vlastního proxy Tor', + 'Sets the address type to display for each wallet' => 'Nastaví typ adres, které se zobrazí pro každou peněženku', + 'Sets the amount type to display in the wallets list' => 'Nastaví typ částek, které se zobrazí v seznamu peněženek', + 'Sets the currency of the displayed prices' => 'Nastaví měnu zobrazovaných cen', + 'Sets the duration of inactivity required for the automatic lock to occur' => 'Nastaví dobu neaktivity potřebnou k aktivaci automatického zámku', + 'Sets the duration of time between price updates' => 'Nastaví dobu mezi jednotlivými aktualizacemi cen', + 'Sets the foreign API secret for the custom node. Leave this empty if the custom node doesn\'t require a foreign API secret' => 'Nastaví tajemství cizího API pro daný vlastní uzel. Pokud váš uzel nepožaduje tajemství cizího API, ponechte toto pole prázdné.', + 'Sets the maximum number of messages that the log can display. Earlier messages will be removed once this limit is reached' => 'Nastaví maximální počet zpráv, které může protokol zobrazit. Po dosažení tohoto limitu budou starší zprávy smazány', + /*TODO*///'Sets the network type to use' => '', + 'Sets the number of confirmations required for a new output to be spendable' => 'Nastaví počet potvrzení potřebných k tomu, aby byl nový výstup transakce k dispozici', + /*TODO*///'Sets the wallet type to use' => '', + 'Settings' => 'Nastavení', + 'Settings Error' => 'Chyba Nastavení', + 'Show' => 'Ukázat', + 'Show listener connection error messages' => 'Ukázat chybové hlášky připojení listeneru', + 'Show node connection error messages' => 'Ukázat chybové hlášky připojení uzlu', + 'Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.' => 'Některé prohlížeče neumožňují připojení k obsahu, který je poskytován nezabezpečeně, z aplikace, která je poskytována zabezpečeně.', + 'Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.' => 'Některé prohlížeče neumožňují připojení k obsahu, který je poskytován nezabezpečeně, ze stránky, která je poskytována zabezpečeně.', + /*TODO*///'Source code:' => '', + 'Spendable' => 'K dispozici', + 'Spendable amount:' => 'Částka k dispozici:', + 'Spendable height:' => 'Výška bloku k dispozici:', + 'Status:' => 'Stav:', + 'Successfully connected to the listener.' => 'Připojení k listeneru proběhlo úspěšně.', + 'Successfully connected to the node.' => 'Připojení k uzlu proběhlo úspěšně.', + 'Successfully connected to the prices server.' => 'Připojení k cenovému serveru proběhlo úspěšně.', + 'Successfully updated the prices.' => 'Ceny úspěšně aktualizovány.', + 'Synced' => 'Synchronizováno', + 'Syncing' => 'Synchronizuje se', + 'Syncing…' => 'Synchronizuje se…', + 'Syncing failed' => 'Synchronizace se nezdařila', + 'Testnet' => 'Testnet', + /*TODO*///'That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.' => '', + /*TODO*///'That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.' => '', + /*TODO*///'That file is invalid, contains unsupported features, or was already used.' => '', + /*TODO*///'That hardware wallet is currently in use.' => '', + 'That hardware wallet isn\'t for %1$y.' => 'Daná hardware peněženka není pro %1$y.', + 'That hardware wallet isn\'t for Wallet %1$s.' => 'Daná hardware peněženka není pro Peněženka %1$s.', + /*TODO*///'That hardware wallet was disconnected.' => '', + /*TODO*///'That QR code is invalid.' => '', + 'The address for %1$y was successfully copied to your clipboard.' => 'Adresa pro %1$y byla úspěšně zkopírována do vaší schránky.', + 'The address for Wallet %1$s was successfully copied to your clipboard.' => 'Adresa pro Peněženka %1$s byla úspěšně zkopírována do vaší schránky.', + /*TODO*///'The address was successfully copied to your clipboard.' => '', + 'The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.' => 'Aplikace na dané hardware peněžence %1$x není kompatibilní. Pro pokračování aktualizujte aplikaci na dané hardware peněžence na verzi %2$v nebo novější.', + 'The current height is unknown.' => 'Aktuální výška bloku není známa.', + 'The database failed.' => 'Databáze selhala.', + 'The fee changed.' => 'Poplatek se změnil.', + 'The fee is invalid.' => 'Poplatek je neplatný.', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.' => '', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.' => '', + 'The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.' => 'Na překladu této aplikace se podíleli následující lidé. Pošlete mail %1$m, pokud máte zájem o překlad této aplikace do jiného jazyka.', + 'The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.' => 'Na překladu tohoto rozšíření se podíleli následující lidé. Pošlete mail %1$m, pokud máte zájem o překlad tohoto rozšíření do jiného jazyka.', + 'The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.' => 'Na překladu této stránky se podíleli následující lidé. Pošlete mail %1$m, pokud máte zájem o překlad této stránky do jiného jazyka.', + 'The hardware wallet is already connected.' => 'Hardware peněženka je již připojena.', + 'The ID for transaction %1$s was successfully copied to your clipboard.' => 'ID transakce %1$s bylo úspěšně zkopírováno do vaší schránky.', + 'The node isn\'t compatible. The node\'s version must be version %1$v or newer.' => 'Daný uzel není kompatibilní. Verze uzlu musí být %1$v nebo novější.', + 'The node may require a foreign API secret.' => 'Daný uzel možná bude požadovat tajemství cizího API.', + 'The node returned an invalid response.' => 'Daný uzel vrátil neplatnou odpověď.', + 'The node returned an unauthorized response.' => 'Daný uzel vrátil neautorizovanou odpověď.', + 'The node\'s current height is %1$s.' => 'Aktuální výška daného uzlu je %1$s.', + 'The node\'s new height is %1$s.' => 'Nová výška daného uzlu je %1$s.', + 'The node\'s version is %1$v.' => 'Verze uzlu je %1$v.', + 'The passphrase for %1$y is the following passphrase.' => 'Bezpečnostní fráze pro %1$y je následující.', + 'The passphrase for Wallet %1$s is the following passphrase.' => 'Bezpečnostní fráze pro Peněženka %1$s je následující.', + 'The payment proof address for %1$y is the following payment proof address.' => 'Adresa potvrzení o platbě pro %1$y je následující.', + 'The payment proof address for Wallet %1$s is the following payment proof address.' => 'Adresa potvrzení o platbě pro Peněženka %1$s je následující.', + /*TODO*///'The recipient payment proof address you used for the transaction is the following payment proof address.' => '', + 'The recipient responded with the following invalid response.' => 'Příjemce odpověděl následující neplatnou odpovědí.', + /*TODO*///'The sender payment proof address you\'re using for the transaction is the following payment proof address.' => '', + 'The setting doesn\'t exist.' => 'Dané nastavení neexistuje.', + 'The transaction can\'t be rebroadcast.' => 'Daná transakce nemůže být znovuvyslána.', + 'The transaction doesn\'t belong to the wallet.' => 'Daná transakce nepatří k této peněžence.', + 'The transaction doesn\'t exist.' => 'Daná transakce neexistuje.', + /*TODO*///'The transaction doesn\'t have a file response or its file response isn\'t known.' => '', + /*TODO*///'The transaction doesn\'t have a payment proof.' => '', + 'The transaction is already canceled.' => 'Daná transakce již byla zrušena.', + /*TODO*///'The transaction\'s sender payment proof address is the following payment proof address.' => '', + 'The transaction was successfully canceled.' => 'Daná transakce byla úspěšně zrušena.', + 'The transaction was successfully rebroadcast.' => 'Daná transakce byla úspěšně znovuvyslána.', + /*TODO*///'The transaction won\'t have a payment proof.' => '', + 'The update was successfully installed. This app will now reload to use the new version.' => 'Aktualizace byla úspěšně nainstalována. Nyní se aplikace znovu načte a bude spuštěna její nová verze.', + 'The update was successfully installed. This site will now reload to use the new version.' => 'Aktualizace byla úspěšně nainstalována. Nyní se stránka znovu načte a bude spuštěna její nová verze.', + 'The value was successfully copied to your clipboard.' => 'Hodnota byla úspěšně zkopírována do vaší schránky.', + 'The wallet doesn\'t exist.' => 'Tato peněženka neexistuje.', + 'The wallet is closed.' => 'Tato peněženka je zavřena.', + 'The wallet isn\'t synced.' => 'Tato peněženka není sesynchronizována.', + 'The wallet\'s address doesn\'t exist.' => 'Adresa peněženky neexistuje.', + 'The wallets are locked.' => 'Peněženky jsou uzamčeny.', + 'The wallet was successfully renamed.' => 'Peněženka byla úspěšně přejmenována.', + 'Third-Party Cookies Information' => 'Informace o cookies třetích stran', + 'This app is currently down for maintenance.' => 'Tato aplikace momentálně kvůli údržbě není k dispozici.', + 'This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.' => 'Tato aplikace je poskytována „tak, jak je“, bez záruky jakéhokoli druhu, výslovné nebo předpokládané, včetně, ale nikoli výhradně, záruk obchodovatelnosti, vhodnosti pro konkrétní účel a neporušení práv. Autoři nebo držitelé autorských práv v žádném případě nenesou odpovědnost za jakékoli nároky, škody nebo jinou odpovědnost, ať už v případě smluvního jednání, deliktu či jinak, vyplývající z této aplikace, nebo ze spojitostí s touto aplikací, případně používáním či jinými jednáními s touto aplikací.', + /*TODO*///'This app must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This app must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This app utilizes code and assets from the following sources.' => 'Tato aplikace využívá kód a prostředky z následujících zdrojů.', + 'This app will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Z důvodu plánované údržby tato aplikace nebude od %1$d v %2$t k dispozici.', + 'This app won\'t run when it\'s embedded in a site.' => 'Tato aplikace se nespustí, pokud je zasazena na web.', + 'This extension injects an API into every website you visit making it possible for those websites to interact with MWC Wallet.' => 'Toto rozšíření vkládá rozhraní API do každé webové stránky, kterou navštívíte, což těmto stránkám umožňuje interakci s MWC Wallet.', + 'This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.' => 'Toto rozšíření je poskytováno „tak, jak je“, bez záruky jakéhokoli druhu, výslovné nebo předpokládané, včetně, ale nikoli výhradně, záruk obchodovatelnosti, vhodnosti pro konkrétní účel a neporušení práv. Autoři nebo držitelé autorských práv v žádném případě nenesou odpovědnost za jakékoli nároky, škody nebo jinou odpovědnost, ať už v případě smluvního jednání, deliktu či jinak, vyplývající z tohoto rozšíření, nebo ze spojitostí s tímto rozšířením, případně používáním či jinými jednáními s tímto rozšířením.', + /*TODO*///'This extension must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This extension must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This extension utilizes code and assets from the following sources.' => 'Toto rozšíření využívá kód a prostředky z následujících zdrojů.', + 'This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Toto rozšíření nebude fungovat správně, pokud je váš prohlížeč nakonfigurován k blokování cookies třetích stran. Před pokračováním se ujistěte, že váš prohlížeč povoluje cookies třetích stran.', + 'This extension won\'t run when it\'s embedded in a site.' => 'Toto rozšíření se nespustí, pokud je zasazeno na web.', + 'This may take several minutes to complete. The recipient must be online and listening at that address to receive this payment.' => 'Tato akce může zabrat několik minut. Příjemce musí být na dané adrese online a připojen k listeneru, aby mohl platbu přijmout.', + 'This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.' => 'Tato bezpečnostní fráze vám umožní obnovit Peněženka %1$s. Doporučuje se zaznamenat si tuto bezpečnostní frázi v zabezpečené nedigitální podobě.', + 'This site is currently down for maintenance.' => 'Tato stránka momentálně kvůli údržbě není k dispozici.', + 'This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.' => 'Tato stránka je poskytována „tak, jak je“, bez záruky jakéhokoli druhu, výslovné nebo předpokládané, včetně, ale nikoli výhradně, záruk obchodovatelnosti, vhodnosti pro konkrétní účel a neporušení práv. Autoři nebo držitelé autorských práv v žádném případě nenesou odpovědnost za jakékoli nároky, škody nebo jinou odpovědnost, ať už v případě smluvního jednání, deliktu či jinak, vyplývající z této stránky, nebo ze spojitostí s touto stránkou, případně používáním či jinými jednáními s touto stránkou.', + /*TODO*///'This site must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This site must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This site uses cookies that are essential to its functionality. By using this site\'s services or clicking OK, you agree to this site\'s use of cookies.' => 'Tato stránka používá cookies, které jsou nezbytné pro její správné fungování. Používáním služeb této stránky nebo kliknutím na "OK" souhlasíte s využíváním cookies touto stránkou.', + 'This site utilizes code and assets from the following sources.' => 'Tato stránka využívá kód a prostředky z následujících zdrojů.', + 'This site will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Z důvodu plánované údržby tato stránka nebude od %1$d v %2$t k dispozici.', + 'This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.' => 'Tato stránka nebude správně fungovat, pokud máte zapnut režim soukromého nebo anonymního prohlížení nebo pokud je váš prohlížeč nakonfigurován tak, aby automaticky odstraňoval soubory cookie a data stránek. Než budete pokračovat, ujistěte se, že jsou zakázány soukromé a anonymní režimy procházení a že je váš prohlížeč nakonfigurován tak, aby uchovával soubory cookie a data stránek.', + 'This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Tato stránka nebude fungovat správně, pokud je váš prohlížeč nakonfigurován k blokování cookies třetích stran. Před pokračováním se ujistěte, že váš prohlížeč povoluje cookies třetích stran.', + 'This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.' => 'Tato stránka se nespustí, pokud je zasazena na web. Pro pokračování navštivte %1$l.', + 'This wallet can only be recovered by using its passphrase once it\'s been deleted.' => 'Tato peněženka může být obnovena bezpečnostní frází pouze po té, co byla smazána.', + /*TODO*/'This wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Tato peněženka může být obnovena bezpečnostní frází pouze po té, co byla smazána.', + /*TODO*///'This wallet is available free of charge and should not be sold in any format. If you paid to access this wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back.' => '', + 'Time to live cut off height must be greater than or equal to the lock height.' => 'Výška přerušení životnosti transakce musí být větší nebo rovna výšce zámku.', + 'Time to live cut off height must be greater than the current height.' => 'Výška přerušení životnosti transakce musí být větší než aktuální výška.', + 'Tor Proxy Settings' => 'Nastavení Tor proxy', + 'Transaction %1$s' => 'Transakce %1$s', + 'Transaction Canceled' => 'Transakce zrušena', + 'Transaction Error' => 'Chyba transakce', + 'Transaction Rebroadcast' => 'Znovuvyslání transakce', + 'Transactions' => 'Transakce', + 'Transactions Navigation Error' => 'Chyba navigace transakcí', + 'Translation Contributors' => 'Na překladu se podíleli', + /*TODO*///'Trezor' => '', + /*TODO*///'Trezor Model One' => '', + /*TODO*///'Trezor Model T' => '', + /*TODO*///'Trezor Safe 3' => '', + /*TODO*///'Trezor Safe 5' => '', + 'Trying to connect to the listener at %1$y.' => 'Pokouším se připojit k listeneru na %1$y.', + 'Trying to connect to the node at %1$y.' => 'Pokouším se připojit k uzlu na %1$y.', + 'Trying to connect to the prices server at %1$y.' => 'Pokouším se připojit k cenovému serveru na %1$y.', + 'Type:' => 'Typ:', + 'Unauthorized' => 'Neautorizováno', + 'Unauthorized response from the recipient.' => 'Neautorizovaná odpověď od příjemce.', + 'Unbroadcast transactions can\'t be rebroadcast.' => 'Nevyslané transakce nemohou but znovuvyslány.', + 'Unconfirmed' => 'Nepotvrzeno', + 'Unconfirmed amount:' => 'Nepotvrzená částka:', + 'Unknown' => 'Neznámý', + 'Unlock' => 'Odemknout', + 'Unlocked.' => 'Odemknuto.', + 'Unlock Error' => 'Chyba odemknutí', + /*TODO*///'Unlock that hardware wallet to continue connecting to it.' => '', + 'Unlock the hardware wallet for %1$y to continue getting the payment proof address.' => 'Pro pokračování v získávání adresy potvrzení o platbě pro %1$y odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for %1$y to continue mining.' => 'Pro pokračování v těžbě %1$y odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for %1$y to continue receiving a payment.' => 'Pro pokračování v přijímání transakce pro %1$y odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for %1$y to continue sending the payment.' => 'Pro pokračování v odesílání transakce z %1$y odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Pro pokračování v získávání adresy potvrzení o platbě pro Peněženka %1$s odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for Wallet %1$s to continue mining.' => 'Pro pokračování v těžbě Peněženka %1$s odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Pro pokračování v přijímání transakce pro Peněženka %1$s odemkněte vaši hardware peněženku.', + 'Unlock the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Pro pokračování v odesílání transakce z Peněženka %1$s odemkněte vaši hardware peněženku.', + 'Unsupported response from the recipient.' => 'Nepodporovaná odpověď příjemce.', + 'Up' => 'Nahoru', + 'Update Available' => 'Je k dispozici aktualizace', + 'Update Installed' => 'Aktualizace nainstalována', + 'Update your browser to use this feature.' => 'Pro využití této funkce aktualizujte prohlížeč.', + 'Updating the prices failed.' => 'Aktualizace cen se nezdařila.', + 'URL' => 'URL', + 'Use custom listener' => 'Použít vlastní listener', + 'Use custom node' => 'Použít vlastní uzel', + 'Use custom Tor proxy' => 'Použít vlastní Tor proxy', + 'Uses a custom listener instead of the default listener' => 'Namísto defaultního listeneru použije vlastní', + 'Uses a custom node instead of the default nodes' => 'Namísto defaultních uzlů použije vlastní', + 'Uses a custom Tor proxy instead of the default Tor proxy' => 'Namísto defaultního Tor proxy použije vlastní', + 'Utilities' => 'Nástroje', + 'Value:' => 'Hodnota:', + 'Value: %1$c' => 'Hodnota: %1$c', + 'Value Copied' => 'Hodnota zkopírována', + 'Value in %1$y' => 'Hodnota v %1$y', + 'Verifying the payment proof address failed.' => 'Ověřování adresy potvrzení o platbě se nezdařilo.', + 'Verifying the Slatepack address on the hardware wallet failed.' => 'Ověřování Slatepack adresy na dané hardware peněžence se nezdařilo.', + 'Verifying the Tor address on the hardware wallet failed.' => 'Ověřování Tor adresy na dané hardware peněžence se nezdařilo.', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no recipient payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the recipient payment proof address displayed matches the following payment proof address.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.' => '', + 'Verify that the node\'s foreign API secret is correct.' => 'Ověřte, že tajemství cizího API daného uzlu je zadané správně.', + 'Verify that the pasted address matches the following address when you paste it.' => 'Při vkládání ověřte, že vložená adresa je totožná s touto.', + 'Verify that the pasted ID matches the following ID when you paste it.' => 'Při vkládání ověřte, že vložené ID je totožné s tímto.', + 'Verify that the pasted value matches the following value when you paste it.' => 'Při vkládání ověřte, že vložená hodnota je totožná s touto.', + 'Verify that the Slatepack address displayed on the hardware wallet matches the following payment proof address.' => 'Ověřte, že Slatepack adresa zobrazená na dané hardware peněžence je totožná s následující adresou potvrzení o platbě.', + 'Verify that the Tor address displayed on the hardware wallet matches the following payment proof address.' => 'Ověřte, že Tor adresa zobrazená na dané hardware peněžence je totožná s následující adresou potvrzení o platbě.', + 'Verify the payment proof address on the hardware wallet for %1$y to continue getting the payment proof address.' => 'Ověřte adresu potvrzení o platbě na hardwarové peněžence, aby mohla %1$y pokračovat v získávání adresy potvrzení o platbě.', + 'Verify the payment proof address on the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Ověřte adresu potvrzení o platbě na hardwarové peněžence, aby mohla Peněženka %1$s pokračovat v získávání adresy potvrzení o platbě.', + 'Version %1$v' => 'Verze %1$v', + 'Version Changes' => 'Změny verze', + 'Version Information' => 'Informace o verzi', + 'Version number:' => 'Číslo verze:', + 'Wallet %1$s' => 'Peněženka %1$s', + 'Wallet Error' => 'Chyba Peněženky', + 'Wallet Renamed' => 'Peněženka přejmenována', + 'Wallets' => 'Peněženky', + 'Wallet Settings' => 'Nastavení Peněženky', + 'Wallet type' => 'Typ Peněženky', + 'Wallet type:' => 'Typ Peněženky:', + 'Website API integration' => 'Integrace API rozhraní webové stránky', + 'Yes' => 'Ano', + 'You aren\'t connected to a listener.' => 'Nejste připojen k listeneru.', + /*TODO*///'You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.' => '', + 'You can guarantee that this payment is going to the intended recipient by having the recipient confirm that this payment proof address is their payment proof address.' => 'Můžete zaručit, že tato platba bude odeslána zamýšlenému příjemci tím, že příjemce potvrdí, že tato adresa potvrzení o platbě je jeho adresou potvrzení o platbě.', + 'You can only receive payments at this address while you\'re online and connected to a listener.' => 'Platby na tuto adresu můžete přijímat pouze pokud jste online a připojen k listeneru.', + /*TODO*///'You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.' => '', + 'You can\'t guarantee that this payment is going to the intended recipient since the transaction doesn\'t have a payment proof.' => 'Vzhledem k tomu, že tato transakce neobsahuje potvrzení o platbě, nelze zaručit, že zamíří k zamýšlenému příjemci.', + /*TODO*///'You\'ll be sending %1$c from %2$y for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Budete odesílat %1$c z %2$y na následující adresu, s poplatkem %3$c.', + /*TODO*///'You\'ll be sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Budete odesílat %1$c z Peněženka %2$s na následující adresu, s poplatkem %3$c.', + 'You\'ll need to provide a Tor proxy address to connect to the node.' => 'Pro připojení k uzlu bude potřeba uvést vaši Tor proxy adresu.', + 'You\'ll need to provide a Tor proxy address to connect to the recipient.' => 'Pro připojení k příjemci bude potřeba uvést vaši Tor proxy adresu.', + 'You\'ll no longer be able to receive payments at the wallet\'s current address once its address suffix has been changed.' => 'Po změně přípony adresy již nebudete moci přijímat platby na stávající adresu peněženky.', + 'You may need to be already paired with the device before this app can connect to it.' => 'Možná budete muset být se zařízením spárováni, než se k němu tato aplikace bude moci připojit.', + 'You may need to be already paired with the device before this extension can connect to it.' => 'Možná budete muset být se zařízením spárováni, než se k němu toto rozšíření bude moci připojit.', + 'You may need to be already paired with the device before this site can connect to it.' => 'Možná budete muset být se zařízením spárováni, než se k němu tato stránka bude moci připojit.', + /*TODO*///'You may need to disconnect and reconnect the hardware wallet to connect to it.' => '', + 'You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.' => 'Možná budete muset toto rozšíření otevřít v záložce nebo okně, pokud nebude schopno se připojit k hardware peněžence.', + 'You may need to specify a different Tor proxy address to connect to the node.' => 'Pro připojení k uzlu budete možná muset zadat jinou adresu Tor proxy.', + 'You may need to specify a different Tor proxy address to connect to the recipient.' => 'Pro připojení k příjemci budete možná muset zadat jinou adresu Tor proxy.', + 'You may need to specify a listener address that is served over HTTPS to connect to the listener.' => 'Pro připojení k listeneru budete možná muset zadat adresu listeneru, která je obsluhována přes HTTPS.', + 'You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.' => 'Pro připojení k uzlu budete možná muset zadat adresu uzlu, která je obsluhována přes HTTPS nebo jako Onion Service.', + 'You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.' => 'Pro připojení k příjemci budete možná muset zadat adresu příjemce, která je obsluhována přes HTTPS nebo jako Onion Service.', + 'Your browser doesn\'t allow using a camera.' => 'Váš prohlížeč nepovoluje používání kamery.', + 'Your browser doesn\'t allow using USB or Bluetooth devices.' => 'Váš prohlížeč nepovoluje používání USB nebo Bluetooth zařízení.', + 'Your browser doesn\'t support JavaScript. Update your browser and/or enable JavaScript to continue.' => 'Váš prohlížeč nepodporuje JavaScript. Pro pokračování aktualizujte prohlížeč a/nebo povolte JavaScript.', + 'Your browser isn\'t compatible. Update your browser to continue.' => 'Váš prohlížeč není kompatibilní. Pro pokračovaní aktualizujte prohlížeč.', + 'You\'re no longer connected to a node.' => 'Již nejste připojen k uzlu.', + 'You\'re no longer connected to the listener.' => 'Již nejste připojen k listeneru.', + 'You\'re no longer connected to the node.' => 'Již nejste připojen k danému uzlu.', + 'You\'re not authorized to connect to the node.' => 'Nejste autorizován pro připojení se k danému uzlu.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction doesn\'t have a payment proof.' => 'Odesíláte %1$c s poplatkem %2$c, a tato platba neobsahuje potvrzení o platbě.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction\'s recipient payment proof address is the following payment proof address.' => 'Odesíláte %1$c s poplatkem %2$c, adresa příjemce pro potvrzení o platbě je následující.', + 'Your password was successfully changed.' => 'Vaše heslo bylo úspěšně změněno.', + 'You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.' => 'Tuto platbu byste neměli považovat za legitimní dokud nebude potvrzena na blockchainu.', + 'You were sent %1$c to %2$y.' => 'Bylo vám odesláno %1$c do %2$y.', + 'You were sent %1$c to %2$y with a message.' => 'Bylo vám odesláno %1$c do %2$y, společně se zprávou.', + 'You were sent %1$c to Wallet %2$s.' => 'Bylo vám odesláno %1$c do Peněženka %2$s.', + 'You were sent %1$c to Wallet %2$s with a message.' => 'Bylo vám odesláno %1$c do Peněženka %2$s, společně se zprávou.', + 'You won\'t be able to change address suffixes without being connected to a listener.' => 'Bez připojení k listeneru nebude možné měnit přípony adresy.', + 'You won\'t be able to rebroadcast transactions without being connected to a node.' => 'Bez připojení k uzlu nebude možné znovuvyslat transakce.', + 'You won\'t be able to receive payments without being connected to a listener.' => 'Bez připojení k listeneru nebude možné přijímat platby.', + 'You won\'t be able to send payments without being connected to a node.' => 'Bez připojení k uzlu nebudete schopni odesílat platby.', + ] + ]; +?> diff --git a/languages/dutch.php b/languages/dutch.php new file mode 100755 index 0000000..f644fb7 --- /dev/null +++ b/languages/dutch.php @@ -0,0 +1,734 @@ + [ + "Ollebollegijs" + ], + + // Constants + "Constants" => [ + + // Language + "Language" => "Nederlands", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/netherlands.svg", + + // Currency + "Currency" => "EUR", + + // Extension locale code + "Extension Locale Code" => "nl", + + // Fallback + "Fallback" => "nl" + ], + + // Text + "Text" => [ + '(?<=,) ' => ' ', + '(?<=.) ' => ' ', + '(?<=:) ' => ' ', + ',(?= )' => ',', + ' (%1$c)' => ' (%1$c)', + '%1$d at %2$t' => '%1$d om %2$t', + '#%1$s' => '#%1$s', + '%1$s%%' => '%1$s%%', + '%1$s–%2$s' => '%1$s–%2$s', + '© %1$s–%2$s Nicolas Flamel.' => '© %1$s–%2$s Nicolas Flamel.', + '© %1$s Nicolas Flamel.' => '© %1$s Nicolas Flamel.', + '%1$u:' => '%1$u:', + '%1$x/%2$x/v%3$v' => '%1$x/%2$x/v%3$v', + '%1$y: %2$m' => '%1$y: %2$m', + '%1$y: %2$m, License: %3$m' => '%1$y: %2$m, Licentie: %3$m', + 'About' => 'Over', + 'About Error' => 'Over fout', + 'Access to your camera was denied.' => 'Toegang tot uw camera is geweigerd.', + 'Account' => 'Rekening', + 'Account Error' => 'Rekening Fout', + 'Address' => 'Adres', + 'Address:' => 'Adres:', + 'Address Copied' => 'Adres gekopieerd', + 'A listener address hasn\'t been provided.' => 'Er is geen luisteraar adres opgegeven.', + 'All' => 'Alle', + 'All Error' => 'Alle Fout', + 'Allow changing base fee' => 'Autoriseer verandering van de basis vergoeding', + 'Allows changing a transaction\'s base fee when sending a payment' => 'Staat het veranderen van de basisvergoeding van een transactie toe bij het zenden van een betaling', + 'Allows processing mining related API requests' => 'Staat het verwerken van aan mijnen gerelateerde API verzoeken toe', + 'A MimbleWimble Coin wallet.' => 'Een MimbleWimble Coin portemonnee.', + 'Amount' => 'Hoeveelheid', + 'Amount:' => 'Hoeveelheid:', + 'Amount: %1$c' => 'Hoeveelheid: %1$c', + 'Amount is empty.' => 'Hoeveelheid is leeg.', + 'Amount is invalid.' => 'Hoeveelheid is ongeldig.', + /*TODO*///'Amount\'s value when recorded:' => '', + 'An error has occurred. This app will automatically reload in a few seconds.' => 'Er heeft een fout plaatsgevonden. Deze app zal binnen een aantal seconden automatisch herladen.', + 'An error has occurred. This site will automatically reload in a few seconds.' => 'Er heeft een fout plaatsgevonden. Deze site zal binnen een aantal seconden automatisch herladen.', + 'An error occurred while trying to access your camera.' => 'Tijdens het benaderen van uw camera heeft er een fout plaatsgevonden.', + /*TODO*///'An internal error occurred.' => '', + 'A node address hasn\'t been provided.' => 'Er is geen node adres verstrekt.', + 'An operation is currently being performed on the wallet\'s address suffix.' => 'Momenteel wordt er een operatie uitgevoerd op het achtervoegsel van de portemonnee.', + 'An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.' => 'Er is een update voor deze app beschikbaar. Wilt u de update nu installeren? Zo ja, dan zal de app herladen worden wanneer de update geïnstalleerd is. Zo nee, dan zal de update de volgende keer dat u deze app opent geïnstalleerd worden.', + 'An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.' => 'Er is een update voor deze site beschikbaar. Wilt u de update nu installeren? Zo ja, dan zal de site herladen worden wanneer de update geïnstalleerd is. Zo nee, dan zal de update de volgende keer dat u deze site bezoekt geïnstalleerd worden.', + 'API Settings' => 'API instellingen', + 'Approve exporting the root public key for the account at index %1$s on that hardware wallet.' => 'Sta het exporteren van de root publieke sleutel van de rekening op index %1$s op die hardware portemonnee toe.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue mining.' => 'Sta het ontvangen van een transactie op de hardware portemonnee voor %1$y toe om door te gaan met mijnen.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.' => 'Sta het ontvangen van een transactie op de hardware portemonnee voor %1$y toe om door te gaan met het ontvangen van een betaling.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.' => 'Sta het ontvangen van een transactie op de hardware portemonnee voor Portemonnee %1$s toe om door te gaan met mijnen.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Sta het ontvangen van een transactie op de hardware portemonnee voor Portemonnee %1$s toe om door te gaan met het ontvangen van een betaling.', + /*TODO*///'Approve Receiving Payment' => '', + 'Approve sending the transaction on the hardware wallet for %1$y to continue sending the payment.' => 'Sta het zenden van de transactie op de hardware portemonnee voor %1$y toe om door te gaan met het zenden van de betaling.', + 'Approve sending the transaction on the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Sta het zenden van de transactie op de hardware portemonnee voor Portemonnee %1$s toe om door te gaan met het zenden van de betaling.', + 'Approving the transaction on the hardware wallet was denied.' => 'Het goedkeuren van de transactie op de hardware portemonnee is geweigerd.', + 'Are you sure you want to cancel transaction %1$s?' => 'Weet u zeker dat u transactie %1$s wilt annuleren?', + 'Are you sure you want to change the address suffix for %1$y?' => 'Weet u zeker dat u het adres achtervoegsel wilt veranderen voor %1$y?', + 'Are you sure you want to change the address suffix for Wallet %1$s?' => 'Weet u zeker dat u het adres achtervoegsel voor Portemonnee %1$s wilt veranderen?', + 'Are you sure you want to delete %1$y?' => 'Weet u zeker dat u %1$y wilt verwijderen?', + 'Are you sure you want to delete all your wallets?' => 'Weet u zeker dat u al uw portemonnees wilt verwijderen?', + 'Are you sure you want to delete Wallet %1$s?' => 'Weet u zeker dat u Portemonnee %1$s wilt verwijderen?', + 'Are you sure you want to exit? There\'s a remaining transaction.' => 'Weet u zeker dat u wilt stoppen? Er is een resterende transactie.', + 'Are you sure you want to exit? There\'s remaining transactions.' => 'Weet u zeker dat u wilt stoppen? Er zijn resterende transacties.', + 'Are you sure you want to reset the settings to their default values?' => 'Weet u zeker dat u de instellingen wilt resetten naar de standaard waarden?', + 'Are you sure you want to resync %1$y?' => 'Weet u zeker dat u %1$y wilt hersynchroniseren?', + 'Are you sure you want to resync Wallet %1$s?' => 'Weet u zeker dat u Portemonnee %1$s wilt hersynchroniseren?', + 'Attributions' => 'Attributen', + /*TODO*///'Automatically approve receiving payments' => '', + /*TODO*///'Automatically approves all received payments without requiring manual approval' => '', + 'Automatically lock after a set duration of inactivity' => 'Automatisch versleutelen na een vastgestelde periode van inactiviteit', + 'Automatically lock when not focused' => 'Automatisch versleutelen wanneer niet gefocust', + 'Automatic lock activated.' => 'Automatisch versleutelen geactiveerd.', + 'A wallet can\'t send payments to itself.' => 'Een portemonnee kan geen betalingen aan zichzelf versturen.', + 'A wallet with the same passphrase already exists in your list of wallets.' => 'Een portemonnee met dezelfde wachtwoordzin komt reeds voor in uw lijst met portemonnees.', + 'A wallet with the same root public key already exists in your list of wallets.' => 'Een portemonnee met dezelfde root publieke sleutel komt reeds voor in uw lijst met portemonnees.', + 'Back' => 'Terug', + 'Bad Gateway' => 'Foutieve Toegangspoort, Gateway', + 'Balance' => 'Balans', + 'Base fee' => 'Basisvergoeding', + 'Base fee is empty.' => 'Basisvergoeding is leeg.', + 'Base fee is invalid.' => 'Basisvergoeding is ongeldig.', + /*TODO*///'Bitcoin:' => '', + 'Broadcasting the transaction failed.' => 'Het uitzenden van de transactie is mislukt.', + 'Broadcasting the transaction failed for the following reason.' => 'Het uitzenden van de transactie is mislukt wegens de volgende reden.', + 'Broadcast transactions can\'t be canceled.' => 'Uitgezonden transacties kunnen niet geannuleerd worden.', + 'Cancel' => 'Annuleer', + 'Canceled' => 'Geannuleerd', + 'Canceled transactions can\'t be rebroadcast.' => 'Uitgezonden transacties kunnen niet opnieuw uitgezonden worden.', + /*TODO*///'Cancel Error' => '', + 'Change Address Suffix' => 'Verander Adres Achtervoegsel', + 'Change Address Suffix Error' => 'Verander Adres Achtervoegsel Fout', + 'Changed %1$y setting.' => 'Instelling %1$y veranderd.', + 'Changed %1$y setting to %2$y.' => 'Instelling %1$y veranderd naar %2$y.', + 'Changed password.' => 'Wachtwoord veranderd.', + 'Change Password' => 'Verander wachtwoord', + 'Change Password Error' => 'Verander wachtwoord fout', + 'Changing the address suffix failed.' => 'Het veranderen van het adres achtervoegsel is mislukt.', + 'Changing the address suffix timed out.' => 'Het veranderen van het adres achtervoegsel is verlopen, timed-out.', + 'Changing your password failed.' => 'Het veranderen van het wachtwoord is mislukt.', + 'coinbase' => 'coinbase', + 'Coinbase' => 'Coinbase', + 'Coinbase transactions can\'t be rebroadcast.' => 'Coinbase transacties kunnen niet heruitgezonden worden.', + /*TODO*///'Coinbase transactions don\'t have a file response.' => '', + 'Compatibility Error' => 'Compatibiliteitsfout', + 'Confirmed' => 'Bevestigd', + 'Confirmed amount:' => 'Bevestigde hoeveelheid:', + 'Confirmed and unconfirmed' => 'Bevestigd en onbevestigd', + 'Confirmed height:' => 'Bevestigde hoogte:', + 'Confirmed transactions can\'t be canceled.' => 'Bevestigde transacties kunnen niet geannuleerd worden.', + 'Confirmed transactions can\'t be rebroadcast.' => 'Bevestigde transacties kunnen niet heruitgezonden worden.', + 'Confirm new password' => 'Bevestig nieuw wachtwoord', + 'Confirm new password is empty.' => 'Bevestig nieuw wachtwoord is leeg.', + 'Confirm Password' => 'Bevestig wachtwoord', + 'Confirm password is empty.' => 'Bevestig wachtwoord is leeg.', + 'Confirm Payment Details' => 'Bevestig details van de betaling', + 'Connect' => 'Verbind', + 'Connecting to a hardware wallet.' => 'Verbinden met een hardware portemonnee.', + 'Connecting to a node failed.' => 'Verbinden met een node is mislukt.', + 'Connecting to that hardware wallet failed.' => 'Verbinden met die hardware portemonnee is mislukt.', + /*TODO*///'Connecting to the host failed.' => '', + 'Connecting to the listener failed.' => 'Verbinden met de luisteraar is mislukt.', + 'Connecting to the node failed.' => 'Verbinden met de node is mislukt.', + 'Connecting to the prices server failed.' => 'Verbinden met de prijzen server is mislukt.', + 'Connecting to the recipient failed.' => 'Verbinden met de ontvanger is mislukt.', + 'Connect the hardware wallet for %1$y to continue getting the payment proof address.' => 'Verbind de hardware portemonnee voor %1$y om door te gaan met het verkrijgen van het betalingsbewijs adres.', + 'Connect the hardware wallet for %1$y to continue mining.' => 'Verbind de hardware portemonnee voor %1$y om door te gaan met mijnen.', + 'Connect the hardware wallet for %1$y to continue receiving a payment.' => 'Verbind de hardware portemonnee voor %1$y om door te gaan met het ontvangen van een betaling.', + 'Connect the hardware wallet for %1$y to continue sending the payment.' => 'Verbind de hardware portemonnee voor %1$y om door te gaan met het zenden van een betaling.', + 'Connect the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Verbind de hardware portemonnee voor Portemonnee %1$s om door te gaan met het ontvangen van het betalingsbewijs adres.', + 'Connect the hardware wallet for Wallet %1$s to continue mining.' => 'Verbind de hardware portemonnee voor Portemonnee %1$s om door te gaan met mijnen.', + 'Connect the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Verbind de hardware portemonnee voor Portemonnee %1$s om door te gaan met het ontvangen van een betaling.', + 'Connect the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Verbind de hardware portemonnee voor Portemonnee %1$s om door te gaan met het zenden van een betaling.', + 'Continue' => 'Ga verder', + 'Copy' => 'Kopieer', + 'Copy Address Error' => 'Kopieer Adres Fout', + 'Copy ID Error' => 'Kopieer ID Fout', + 'Copying the address to your clipboard failed.' => 'Het kopiëren van het adres naar uw klembord is mislukt.', + 'Copying the ID to your clipboard failed.' => 'Het kopiëren van de ID naar uw klembord is mislukt.', + 'Copying the value to your clipboard failed.' => 'Het kopiëren van de waarde naar uw klembord is mislukt.', + 'Copyright' => 'Auteursrecht', + 'Copy Value Error' => 'Kopieer Waarde Fout', + 'Create' => 'Creëer, maak aan', + 'Created hardware wallet Wallet %1$s.' => 'Hardware portemonnee Portemonnee %1$s is aangemaakt.', + 'Created wallet Wallet %1$s.' => 'portemonnee Portemonnee %1$s is aangemaakt.', + 'Create Error' => 'Aanmaak fout', + 'Create Wallet Error' => 'portemonnee Aanmaak Fout', + 'Creating slate failed.' => 'Slate aanmaken is mislukt.', + 'Creating transaction failed.' => 'Aanmaken van transactie is mislukt.', + 'Creating wallet failed.' => 'Aanmaken van portemonnee is mislukt.', + 'Currency of the displayed prices' => 'Valuta van de weergegeven prijzen', + 'Current password' => 'Huidig wachtwoord', + 'Current password is empty.' => 'Huidig wachtwoord is leeg.', + 'Current password is incorrect.' => 'Huidig wachtwoord is onjuist.', + 'Custom listener address' => 'Aangepast luisteraar adres', + 'Custom node address' => 'Aangepast node adres', + 'Custom node foreign API secret' => 'Aangepast node foreign API secret', + 'Custom Tor proxy address' => 'Aangepast Tor proxy adres', + 'Default Base Fee' => 'Standaard basisvergoeding', + 'Delete' => 'Verwijder', + 'Delete All Wallets' => 'Verwijder alle Portemonnees', + 'Delete All Wallets Error' => 'Verwijder alle Portemonnees Fout', + 'Deleted all wallets.' => 'Alle Portemonnees verwijderd.', + 'Deleted wallet %1$y.' => 'Verwijder portemonnee %1$y.', + 'Deleted wallet Wallet %1$s.' => 'portemonnee Portemonnee %1$s verwijderd.', + 'Delete Error' => 'Verwijder Fout', + /*TODO*///'Description' => '', + 'Destination:' => 'Bestemming:', + 'Disclaimer' => 'Vrijwaring', + 'Disconnected from the listener.' => 'Verbinding met de luisteraar is verbroken.', + 'Disconnected from the node.' => 'Verbinding met de node is verbroken.', + 'Displayed address type' => 'Weergegeven adres type', + 'Displayed amount type' => 'Weergegeven hoeveelheid type', + 'Display prices' => 'Weergegeven prijzen', + 'Displays a message when not able to connect to a listener or when disconnected from a listener' => 'Geeft een bericht weer wanneer het niet mogelijk is om te verbinden met een luisteraar of wanneer de verbinding met een luisteraar verbroken is', + 'Displays a message when not able to connect to a node, when a connected node is incompatible, or when disconnected from a node' => 'Geeft een bericht weer wanneer het niet mogelijk is om te verbinden met een node, wanneer een node incompatibel is of wanneer de verbinding met een node verbroken is', + 'Displays prices for amounts' => 'Geeft prijzen voor hoeveelheden weer', + /*TODO*///'Donate' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this app.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this extension.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this site.' => '', + 'Don\'t disclose this passphrase to anyone.' => 'Toon de wachtwoordzin aan niemand.', + 'Down' => 'Buiten gebruik', + /*TODO*///'Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*///'Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*/'Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Wanneer een portemonnee verwijderd is, kan deze enkel hersteld worden door gebruik te maken van de bijbehorende wachtwoordzin.', + 'Enable mining API' => 'Activeer mining API', + 'Enter a new name for %1$y.' => 'Voer een nieuwe naam in voor %1$y.', + 'Enter a new name for Wallet %1$s.' => 'Voer een nieuwe naam in voor Portemonnee %1$s.', + 'Enter a wallet\'s passphrase to recover it.' => 'Voer de wachtwoordzin in van een Portemonnee om deze te herstellen.', + 'Enter your password to continue sending the payment.' => 'Voer uw wachtwoord in om door te gaan met het versturen van de betaling.', + 'Enter your password to get the passphrase for %1$y.' => 'Voer uw wachtwoord in om de wachtwoordzin te verkrijgen voor %1$y.', + 'Enter your password to get the passphrase for Wallet %1$s.' => 'Voer uw wachtwoord in om de wachtwoordzin te verkrijgen voor Portemonnee %1$s.', + /*TODO*///'Enter your pin as the following alphabetic characters to unlock the hardware wallet.' => '', + /*TODO*///'Epic Cash' => '', + /*TODO*///'Epic Cash:' => '', + 'Error' => 'Fout', + 'Error %1$s' => 'Fout %1$s', + 'Error response from the recipient.' => 'Fout reactie van de ontanger.', + 'Expired' => 'Geëxpireerd', + 'Expired transactions can\'t be canceled.' => 'Geëxpireerde transacties kunnen niet geannuleerd worden.', + 'Expired transactions can\'t be rebroadcast.' => 'Geëxpireerde transacties kunnen niet opnieuw uitgezonden worden.', + 'Expire height:' => 'Expiratie hoogte:', + 'Exporting the root public key on that %1$x hardware wallet was denied.' => 'Het exporteren van de root publieke sleutel op die %1$x hardware portemonnee is geweigerd.', + 'Failed to determine the primary instance.' => 'Het bepalen van de primaire instantie is mislukt.', + 'Failed to initialize dependencies.' => 'Het initialiseren van afhankelijkheden is mislukt.', + 'Failed to install or update the service worker.' => 'Het installeren of updaten van de service werker is mislukt.', + 'Failed to load resources. Refresh this site to try again.' => 'Het laden van bronnen is mislukt. Herlaad deze site om opnieuw te proberen.', + 'Failed to load resources. Restart this app to try again.' => 'Het laden van bronnen is mislukt. Herstart deze app om opnieuw te proberen.', + 'Fee:' => 'Vergoeding:', + /*TODO*///'Fee\'s value when recorded:' => '', + 'Finalize' => 'Finaliseer', + 'Finalize the transaction for %1$y to continue sending the payment.' => 'Finaliseer de transactie voor %1$y om door te gaan met het versturen van de betaling.', + 'Finalize the transaction for Wallet %1$s to continue sending the payment.' => 'Finaliseer de transactie voor Portemonnee %1$s om door te gaan met het versturen van de betaling.', + 'Finalizing the slate failed.' => 'Finaliseren van de slate is mislukt.', + 'First' => 'Eerste', + 'Floonet' => 'Floonet', + 'Floonet/testnet' => 'Floonet/testnet', + 'Forbidden' => 'Verboden', + 'Forbidden response from the recipient.' => 'Verboden reactie van de ontvanger.', + 'Forward' => 'Doorsturen', + 'From wallet' => 'Vanuit portemonnee', + 'Gateway Timeout' => 'Gateway Time-out', + /*TODO*///'Get File Response' => '', + /*TODO*///'Get File Response Error' => '', + 'Get Passphrase' => 'Ophalen Wachtwoordzin', + 'Get Passphrase Error' => 'Ophalen Wachtwoordzin Fout', + 'Get Payment Proof Address' => 'Ophalen Betalingsbewijs Adres', + 'Get Payment Proof Address Error' => 'Ophalen Betalingsbewijs Fout', + 'Getting the app information from that %1$x hardware wallet failed.' => 'Ophalen van de app informatie van die %1$x hardware wallet is mislukt.', + /*TODO*///'Getting the features from that %1$x hardware wallet failed.' => '', + 'Getting the payment proof address failed.' => 'Ophalen van het betalingsbewijs adres is mislukt.', + 'Getting the root public key from that %1$x hardware wallet failed.' => 'Ophalen van de root publieke sleutel van die %1$x hardware portemonnee is mislukt.', + 'Getting the seed cookie from that %1$x hardware wallet failed.' => 'Ophalen van de seed cookie van die %1$x hardware portemonnee is mislukt.', + /*TODO*///'Give the %1$m file to the payment\'s recipient and open their response file to continue sending the payment.' => '', + /*TODO*///'Give the %1$m file to the payment\'s sender for them to finalize the transaction.' => '', + 'Grin' => 'Grijns', + 'Grin:' => 'Grijns:', + 'Hardware' => 'Hardware', + 'Hardware wallet' => 'Hardware portemonnee', + 'Hardware Wallet' => 'Hardware Portemonnee', + 'Hardware Wallet Approval Requested' => 'Hardware Portemonnee Goedkeuring Verzocht', + 'Hardware wallet connected' => 'Hardware portemonnee verbonden', + 'Hardware Wallet Disconnected' => 'Hardware Portemonnee verbinding is verbroken', + 'Hardware Wallet Error' => 'Hardware Portemonnee Fout', + 'Hardware Wallet Locked' => 'Hardware Portemonnee Versleuteld', + 'Hardware wallet support' => 'Hardware portemonnee ondersteuning', + 'height locked' => 'hoogte versleuteld', + 'Hide' => 'Verberg', + 'HTTP' => 'HTTP', + 'HTTPS' => 'HTTPS', + 'ID:' => 'ID:', + 'ID Copied' => 'ID gekopieerd', + /*TODO*///'If someone asked you to copy/paste something here you are being scammed!!!' => '', + /*TODO*///'If you paid to access MWC Wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back' => '', + 'Inactive lock activated.' => 'Inactiviteitsversleuteling geactiveerd.', + 'Incorrect password.' => 'Wachtwoord onjuist.', + /*TODO*///'Incorrect pin.' => '', + /*TODO*///'Information' => '', + 'Install Now' => 'Installeer Nu', + 'Install the MWC Wallet app?' => 'Installeer de MWC Portemonnee app?', + 'Insufficient balance.' => 'Balans is onvoldoende.', + 'Interface Settings' => 'Interface Instellingen', + 'Internal error' => 'Interne fout', + 'Internal Server Error' => 'Interne Server Fout', + 'Invalid amount.' => 'Ongeldige hoeveelheid.', + 'Invalid message.' => 'Ongeldig bericht.', + /*TODO*///'Invalid message from the host.' => '', + 'Invalid name.' => 'Ongeldige naam.', + 'Invalid origin.' => 'Ongeldige oorsprong.', + 'Invalid parameters' => 'Ongeldige parameters', + 'Invalid passphrase.' => 'Ongeldige wachtwoordzin.', + /*TODO*///'Invalid pin.' => '', + 'Invalid recipient address.' => 'Ongeldig ontvanger adres.', + 'Invalid request' => 'Ongeldig verzoek', + 'Invalid request.' => 'Ongeldig verzoek.', + 'Invalid request response from the recipient.' => 'Ongeldige reactie van de ontvanger op verzoek.', + 'Invalid response from the recipient.' => 'Ongeldige reactie van de ontvanger.', + 'Invalid wallet.' => 'Ongeldige portemonnee.', + 'JavaScript Error' => 'Javascript Fout', + 'Kernel excess:' => 'Kernel exces:', + 'Language' => 'Taal', + 'Language changed to %1$y.' => 'Taal gewijzigd naar %1$y.', + 'Language\'s currency' => 'Valuta van de taal', + 'Last' => 'Laatste', + 'Ledger Flex' => 'Ledger Flex', + 'Ledger Nano S' => 'Ledger Nano S', + 'Ledger Nano S Plus' => 'Ledger Nano S Plus', + 'Ledger Nano X' => 'Ledger Nano X', + 'Ledger Stax' => 'Ledger Stax', + 'Listener' => 'Luisteraar', + 'Listener connected' => 'Luisteraar is verbonden', + 'Listener disconnected' => 'Verbinding met luisteraar is verbroken', + 'Listener Error' => 'Luisteraar Fout', + 'Listener Settings' => 'Luisteraar Instellingen', + 'Listener settings changed. Disconnecting from the listener.' => 'Instellingen van de luisteraar zijn gewijzigd. Verbinding met luisteraar verbreken.', + 'Loading…' => 'Laden…', + 'Loading Error' => 'Laad Fout', + 'Lock' => 'Versleutel', + 'Locked.' => 'Vesleuteld.', + 'Lock height:' => 'Versleutelingshoogte:', + 'Lock if not focused' => 'Versleutel als niet gefocust', + 'Log' => 'Logboek', + 'Log Error' => 'Logboek Fout', + 'Log initialized.' => 'Logboek Geïnitialiseerd.', + /*TODO*///'Login With Wallet' => '', + 'Mainnet' => 'Hoofdnet', + 'Maintenance' => 'Onderhoud', + 'Make sure that the correct app is open on the hardware wallet.' => 'Verzeker u ervan dat de juiste app geopend is op de hardware portemonnee.', + 'Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.' => 'Verzeker u ervan dat de juiste app geopend is op de hardware portemonnee en dat de hardware portemonnee niet versleuteld is.', + /*TODO*///'Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m' => '', + /*TODO*///'Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m' => '', + /*TODO*///'Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m' => '', + /*TODO*///'Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m' => '', + 'Manage your MimbleWimble Coin' => 'Beheer uw MimbleWimble Coin', + 'Match connection' => 'Connectie overeenstemmen', + 'Maximum number of messages that the log can display' => 'Maximaal aantal berichten dat het logboek kan weergeven', + 'Message:' => 'Bericht:', + 'Method not found' => 'Methode niet gevonden', + 'MimbleWimble Coin' => 'MimbleWimble Coin', + 'MimbleWimble Coin:' => 'MimbleWimble Coin:', + 'Minutes between price updates' => 'Minuten tussen prijs updates', + 'Minutes of inactivity required for automatic lock' => 'Minuten aan benodigde inactiviteit tot automatische versleuteling', + 'MWC Wallet' => 'MWC Portemonnee', + 'MWC Wallet — Error' => 'MWC Portemonnee - Fout', + 'MWC Wallet — Error %1$s' => 'MWC Portemonnee - Fout %1$s', + 'MWC Wallet is an extension that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC portemonnee is een extentie die het mogelijk maakt om uw MimbleWimble Coin in uw webbrowser te beheren.', + /*TODO*///'MWC Wallet is an open-source, self-custodial wallet that allows you to easily send and receive MimbleWimble Coin. Designed with ease of use and accessibility in mind, this wallet runs on everything from smartwatches to mainframes while providing an intuitive interface that makes using it a breeze.' => '', + 'MWC Wallet is a self-custodial web wallet that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC portemonnee is een web portemonnee -onder uw eigen beheer- die het mogelijk maakt om uw MimbleWimble Coin in uw webbrowser te beheren.', + 'MWC Wallet — Maintenance' => 'MWC Wallet - Onderhoud', + 'N/A' => 'N/A', + 'Name' => 'Naam', + 'Network changed. Disconnecting from the listener.' => 'Netwerk is gewijzigd. Verbinding met luisteraar verbreken.', + 'Network type' => 'Netwerktype', + 'Network type:' => 'Netwerktype:', + 'New password' => 'Nieuw wachtwoord', + 'New password is empty.' => 'Het nieuwe wachtwoord is leeg.', + 'New passwords don\'t match.' => 'Nieuwe wachtwoorden komen niet overeen.', + 'New Wallet Passphrase' => 'Nieuwe Portemonnee Wachtwoordzin', + 'Next' => 'Volgende', + 'Nicolas Flamel' => 'Nicolas Flamel', + 'No' => 'Niet', + 'No camera was found. Connect a camera to use this feature.' => 'Geen camera gevonden. Verbind een camera om deze functie te gebruiken', + 'Node' => 'Node', + 'Node connected' => 'Node verbonden', + 'Node disconnected' => 'Verbinding met node is verbroken', + 'Node Error' => 'Node Fout', + 'Node Settings' => 'Node Instellingen', + 'Node settings changed. Disconnecting from the node.' => 'Node instellingen gewijzigd. Verbinding met node verbreken', + 'Node warning' => 'Node waarschuwing', + 'No hardware wallet was selected.' => 'Er is geen hardware portemonnee geselecteerd.', + 'Non-hardware wallet' => 'Niet-hardware wallet', + 'no recent duplicate' => 'Geen recent duplicaat', + 'Not compatible with node versions less than %1$v.' => 'Niet compatibel met node versies lager dan %1$v.', + 'Not Found' => 'Niet Gevonden', + 'Not found response from the recipient.' => 'Niet gevonden reactie van ontvanger.', + 'No transactions exist for this wallet.' => 'Geen bestaande transacties voor deze portemonnee.', + 'Number of confirmations:' => 'Aantal bevestigingen:', + 'Number of confirmations required to spend an output' => 'Aantal benodigde bevestigingen om een ouput te kunnen uitgeven', + 'OK' => 'OK', + 'Onion Service' => 'Onion Service', + 'Only one instance of this app can be open at once. Close all other instances to continue.' => 'Er kan maar een enkele instantie van deze app gelijktijdig geopend zijn. Sluit alle andere instanties om door te gaan', + 'Only one instance of this extension can be open at once. Close all other instances to continue.' => 'Er kan maar een enkele instantie van deze extentie gelijktijdig geopend zijn. Sluit alle andere instanties om door te gaan.', + 'Only one instance of this site can be open at once. Close all other instances to continue.' => 'Er kan maar een enkele instantie van deze site gelijktijdig geopend zijn. Sluit alle andere instanties om door te gaan', + /*TODO*///'Opening that file failed.' => '', + 'Open in New Tab' => 'Open in Nieuw Tabblad', + 'Open in New Window' => 'Open in Nieuw Window', + /*TODO*///'Open Response File' => '', + 'Optional message' => 'Optioneel bericht', + 'Order Error' => 'Order Fout', + 'Output commitment:' => 'Uitvoer verplichting:', + 'Passphrase' => 'Wachtwoordzin', + 'Passphrases can\'t be retrieved from hardware wallets.' => 'Wachtwoordzinnen kunnen hiet verkregen worden vanaf hardware portemonnees.', + 'Password' => 'Wachtwoord', + 'Password Changed' => 'Wachtwoord Gewijzigd', + 'Password is empty.' => 'Wachtwoord is leeg.', + 'Passwords don\'t match.' => 'Wachtwoorden komen niet overeen.', + 'Payload too large response from the recipient.' => '-Nettolading te groot- reactie van de ontvanger.', + 'Payment Details' => 'Betalingsdetails', + 'Payment Proof' => 'Betalingsbewijs', + 'Payment Proof Address' => 'Betalingsbewijs Adres', + 'Payment Received' => 'Betaling Ontvangen', + /*TODO*///'Pin' => '', + 'plain' => 'kaal', + 'Previous' => 'Vorige', + 'Private Browsing And Site Data Information' => 'Private Browser En Site Data Informatie', + /*TODO*///'Rebroadcast' => '', + /*TODO*///'Rebroadcast Error' => '', + 'Rebroadcasting the transaction failed.' => 'Opnieuw uitzenden van de transactie is mislukt.', + 'Rebroadcasting the transaction failed for the following reason.' => 'Opnieuw uitzenden van de transactie is mislukt om de volgende reden.', + 'Received' => 'Ontvangen', + 'Received an invalid response from the node.' => 'Onjuiste reactie van de node ontvangen.', + 'Received an invalid response from the prices server.' => 'Onjuiste reactie van de prijzen server ontvangen.', + 'Received transactions can\'t be rebroadcast.' => 'Ontangen transacties kunnen niet opnieuw uitgezonden worden.', + /*TODO*///'Receive Payment As File' => '', + /*TODO*///'Receive Payment As File Error' => '', + 'Recipient address' => 'Ontvangstadres', + 'Recipient address is empty.' => 'Ontvangsadres is leeg.', + 'Recipient address is invalid.' => 'Ontvangstadres is ongeldig.', + 'Recipient address isn\'t supported.' => 'Ontvangstadres wordt niet ondersteund.', + 'Recipient doesn\'t support any available slate versions.' => 'Ontvanger ondersteunt geen van de beschikbare slate versies.', + 'Recipient doesn\'t support payment proofs.' => 'Ontvanger ondersteunt geen betalingsbewijzen.', + 'Recipient payment proof address:' => 'Betalingsbewijs adres ontvanger:', + 'Recipient payment proof signature:' => 'Betalingsbewijs handtekening ontvanger:', + 'Recipient\'s foreign API version isn\'t supported.' => 'Ontvangers foreign API versie wordt niet ondersteund.', + 'Recipient\'s slate versions aren\'t supported.' => 'Slate versies van de ontvanger worden niet ondersteund.', + 'Recorded time:' => 'Opgeslagen tijd:', + 'Recover' => 'Herstel', + 'Recovered wallet Wallet %1$s.' => 'Herstel portemonnee Portemonnee %1$s.', + 'Recover Wallet' => 'Herstel Portemonnee', + 'Recover Wallet Error' => 'Herstel Portemonnee Fout', + 'Refresh this site to try again.' => 'Herlaad deze site om het opnieuw te proberen.', + 'Relative height is invalid.' => 'Relatieve hoogte is ongeldig.', + 'Release date:' => 'Uitgifte datum:', + /*TODO*///'Reload Required' => '', + 'Remind me later' => 'Herinner me later', + 'Rename' => 'Hernoem', + 'Renamed wallet %1$y to %2$y.' => 'Portemonnee %1$y hernoemd naar %2$y.', + 'Renamed wallet Wallet %1$s to %2$y.' => 'Portemonnee Portemonnee %1$s hernoemd naar %2$y.', + 'Rename Error' => 'Hernoemingsfout', + 'Required number of confirmations:' => 'Benodigd aantal bevestigingen:', + 'Require payment proof' => 'Benodigd betalingsbewijs', + 'Requires all new transactions to have a payment proof' => 'Verplicht alle nieuwe transacties een betalingsbewijs te hebben', + 'Reset settings.' => 'Reset instellingen.', + 'Reset Settings' => 'Reset Instellingen', + 'Reset Settings Error' => 'Reset Instellingen Fout', + 'Restart this app to try again.' => 'Herstart deze app om het opnieuw te proberen.', + 'Restart this extension to try again.' => 'Herstart deze extentie om het opnieuw te proberen.', + 'Resync' => 'Hersynchroniseer', + 'Resync Error' => 'Hersynchronisatie Fout', + 'Resyncing can also use a lot of data depending on the number of unspent transaction outputs present in the blockchain.' => 'Hersynchroniseren kan veel data gebruiken afhankelijk van het aantal onuitgegeven transacties die in de blockchain aanwezig zijn.', + 'Resyncing can take a significant amount of time to complete, and you won\'t be able to send payments from this wallet until it\'s finished resyncing.' => 'Hersynchroniseren kan een aanzienlijke tijd duren om om af te ronden. Het zenden van betalingen vanuit deze portemonnee is niet mogelijk totdat het hersynchroniseren is afgerond.', + 'Scan QR Code' => 'Scan QR Code', + 'Scan QR Code Error' => 'Scan QR Code Fout', + 'Selected' => 'Geselecteerd', + 'Select passphrase\'s origin.' => 'Selecteer herkomst wachtwoordzin.', + 'Send' => 'Verstuur', + /*TODO*///'Send as file' => '', + 'Sender payment proof address:' => 'Betalingsbewijs adres zender:', + 'Send Error' => 'Verzend Fout', + /*TODO*///'Sending %1$c from %2$y for a fee of %3$c.' => '', + 'Sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Versturen %1$c vanuit %2$y naar het volgende adres voor een vergoeding van %3$c.', + /*TODO*///'Sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'Sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Versturen %1$c vanuit Portemonnee %2$s naar het volgende adres voor een vergoeding van %3$c.', + 'Sending Payment' => 'Versturen Betaling', + 'Send Payment' => 'Verstuur Betaling', + 'Send Payment Error' => 'Verstuur Betaling Fout', + 'Sent' => 'Verstuur', + /*TODO*///'Sent transactions don\'t have a file response.' => '', + /*TODO*///'Set Payment Proof Address Index' => '', + 'Sets the address for the custom listener' => 'Stelt het adres van de aangepaste luisteraar in', + 'Sets the address for the custom node' => 'Stelt het adres van de aangepaste node in', + 'Sets the address for the custom Tor proxy' => 'Stelt het adres van de aangepaste Tor proxy in', + 'Sets the address type to display for each wallet' => 'Stelt het type adres in dat weergegeven wordt voor iedere portemonnee', + 'Sets the amount type to display in the wallets list' => 'Stelt het type hoeveelheid in dat weergegeven wordt in de portemonnee lijst', + 'Sets the currency of the displayed prices' => 'Stelt de valuta in van de weergegeven prijzen', + 'Sets the duration of inactivity required for the automatic lock to occur' => 'Stelt de duur van de benodigde inactiviteit in voor het optreden van de automatische vergrendeling', + 'Sets the duration of time between price updates' => 'Stelt de tijdsduur in tussen prijsupdates', + 'Sets the foreign API secret for the custom node. Leave this empty if the custom node doesn\'t require a foreign API secret' => 'Stelt het foreign API secret in voor de aangepaste node. Laat dit leeg als de aangepaste node geen foreign API secret benodigd.', + 'Sets the maximum number of messages that the log can display. Earlier messages will be removed once this limit is reached' => 'Stelt het maximale aantal berichten in dat het logboek kan weergeven. Eerdere berichten zullen worden verwijderd indien deze limiet is bereikt.', + /*TODO*///'Sets the network type to use' => '', + 'Sets the number of confirmations required for a new output to be spendable' => 'Stelt het aantal bevestigingen in dat benodigd is voor een nieuwe output om uitgeefbaar te zijn', + /*TODO*///'Sets the wallet type to use' => '', + 'Settings' => 'Instellingen', + 'Settings Error' => 'Instellingen Fout', + 'Show' => 'Toon', + 'Show listener connection error messages' => 'Toon foutberichten van de luisteraar verbinding', + 'Show node connection error messages' => 'Toon foutberichten van de node verbinding', + 'Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.' => 'Sommige browsers staan niet toe om verbinding te maken met content die onveilig geserveerd wordt vanuit een app welke veilig geserveerd wordt.', + 'Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.' => 'Sommige browsers staan niet toe om verbinding te maken met content die onveilig geserveerd wordt vanuit een site welke veilig geserveerd wordt.', + /*TODO*///'Source code:' => '', + 'Spendable' => 'Uitgeefbaar', + 'Spendable amount:' => 'Uitgeefbare hoeveelheid:', + 'Spendable height:' => 'Uitgeefbare hoogte:', + 'Status:' => 'Status:', + 'Successfully connected to the listener.' => 'Succesvol verbonden met de luisteraar.', + 'Successfully connected to the node.' => 'Succesvol verbonden met de node.', + 'Successfully connected to the prices server.' => 'Succesvol verbonden met de prijzen server.', + 'Successfully updated the prices.' => 'De prijzen zijn succesvol bijgewerkt.', + 'Synced' => 'Gesynchroniseerd', + 'Syncing' => 'Synchroniseren', + 'Syncing…' => 'Synchroniseren…', + 'Syncing failed' => 'Synchronisatie is mislukt', + 'Testnet' => 'Testnet', + /*TODO*///'That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.' => '', + /*TODO*///'That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.' => '', + /*TODO*///'That file is invalid, contains unsupported features, or was already used.' => '', + /*TODO*///'That hardware wallet is currently in use.' => '', + 'That hardware wallet isn\'t for %1$y.' => 'Die hardware portemonnee is niet voor %1$y.', + 'That hardware wallet isn\'t for Wallet %1$s.' => 'Die hardware portemonnee is niet voor Portemonnee %1$s.', + /*TODO*///'That hardware wallet was disconnected.' => '', + /*TODO*///'That QR code is invalid.' => '', + 'The address for %1$y was successfully copied to your clipboard.' => 'Het adres voor %1$y is succesvol gekopieerd naar uw klembord.', + 'The address for Wallet %1$s was successfully copied to your clipboard.' => 'Het adres voor Portemonnee %1$s is succesvol gekopieerd naar uw klembord.', + /*TODO*///'The address was successfully copied to your clipboard.' => '', + 'The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.' => 'De app op die %1$x hardware portemonnee is niet compatibel. Update de app op de hardware portemonnee naar versie %2$v of nieuwer om door te gaan.', + 'The current height is unknown.' => 'De huidige hoogte is onbekend.', + 'The database failed.' => 'De database faalde.', + 'The fee changed.' => 'De vergoeding is gewijzigd.', + 'The fee is invalid.' => 'De vergoeding is ongeldig.', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.' => '', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.' => '', + 'The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.' => 'De volgdende mensen hebben de vertalingen voor deze app gemaakt. U kunt %1$m emailen indien u geïnteresseerd bent in het vertalen van deze app naar een andere taal.', + 'The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.' => 'De volgdende mensen hebben de vertalingen voor deze extentie gemaakt. U kunt %1$m emailen indien u geïnteresseerd bent in het vertalen van deze extentie naar een andere taal.', + 'The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.' => 'De volgdende mensen hebben de vertalingen voor deze site gemaakt. U kunt %1$m emailen indien u geïnteresseerd bent in het vertalen van deze site naar een andere taal.', + 'The hardware wallet is already connected.' => 'De hardware portemonnee is reeds verbonden.', + 'The ID for transaction %1$s was successfully copied to your clipboard.' => 'De ID voor transactie %1$s is succesvol gekopieerd naar uw klembord.', + 'The node isn\'t compatible. The node\'s version must be version %1$v or newer.' => 'De node is niet compatibel. De versie van de node moet %1$v of nieuwer zijn', + 'The node may require a foreign API secret.' => 'De node heeft mogelijkerwijs een foreign API secret nodig.', + 'The node returned an invalid response.' => 'De node retourneerde een ongeldige reactie.', + 'The node returned an unauthorized response.' => 'De node retourneerde een ongeautoriseerde reactie.', + 'The node\'s current height is %1$s.' => 'De huidige hoogte van de node is %1$s.', + 'The node\'s new height is %1$s.' => 'De nieuwe hoogte van de node is %1$s.', + 'The node\'s version is %1$v.' => 'De versie van de node is %1$v.', + 'The passphrase for %1$y is the following passphrase.' => 'De wachtwoordzin voor %1$y is de volgende wachtwoordzin.', + 'The passphrase for Wallet %1$s is the following passphrase.' => 'De wachtwoordzin voor Portemonnee %1$s is de volgende wachtwoordzin.', + 'The payment proof address for %1$y is the following payment proof address.' => 'Het betalingsbewijs adres voor %1$y is het volgende betalingsbewijs adres.', + 'The payment proof address for Wallet %1$s is the following payment proof address.' => 'Het betalingsbewijs adres voor Portemonnee %1$s is het volgende betalingsbewijs adres.', + /*TODO*///'The recipient payment proof address you used for the transaction is the following payment proof address.' => '', + 'The recipient responded with the following invalid response.' => 'De ontvanger reageerde met de volgende ongeldige reactie.', + /*TODO*///'The sender payment proof address you\'re using for the transaction is the following payment proof address.' => '', + 'The setting doesn\'t exist.' => 'De instelling bestaat niet.', + 'The transaction can\'t be rebroadcast.' => 'De transactie kan niet opnieuw worden uitgezonden.', + 'The transaction doesn\'t belong to the wallet.' => 'De transactie behoort niet bij de portemonnee.', + 'The transaction doesn\'t exist.' => 'De transactie bestaat niet.', + /*TODO*///'The transaction doesn\'t have a file response or its file response isn\'t known.' => '', + /*TODO*///'The transaction doesn\'t have a payment proof.' => '', + 'The transaction is already canceled.' => 'De transactie is reeds geannuleerd.', + /*TODO*///'The transaction\'s sender payment proof address is the following payment proof address.' => '', + 'The transaction was successfully canceled.' => 'De transactie is succesvol geannuleerd.', + 'The transaction was successfully rebroadcast.' => 'De transactie is succesvol opnieuw uitgezonden.', + /*TODO*///'The transaction won\'t have a payment proof.' => '', + 'The update was successfully installed. This app will now reload to use the new version.' => 'De update is succesvol geïnstalleerd. Deze app zal nu herladen om de nieuwe versie te gebuiken.', + 'The update was successfully installed. This site will now reload to use the new version.' => 'De update is succesvol geïnstalleerd. Deze site zal nu herladen om de nieuwe versie te gebuiken.', + 'The value was successfully copied to your clipboard.' => 'De waarde is succesvol gekopieerd naar uw klembord.', + 'The wallet doesn\'t exist.' => 'De portemonnee bestaat niet.', + 'The wallet is closed.' => 'De portemonnee is gesloten.', + 'The wallet isn\'t synced.' => 'De portemonnee is niet gesynchroniseerd.', + 'The wallet\'s address doesn\'t exist.' => 'Het adres van de portemonnee bestaat niet.', + 'The wallets are locked.' => 'De portemonnees zijn versleuteld.', + 'The wallet was successfully renamed.' => 'De portemonnee is succesvol hernoemd.', + 'Third-Party Cookies Information' => 'Informatie Over Cookies Van Derden', + 'This app is currently down for maintenance.' => 'Deze app is momenteel buiten gebruik voor onderhoud.', + 'This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.' => 'Deze app is verstrekt -zoals deze is-, zonder garantie van enige soort, verwoord of geïmpliceerd, inclusief, maar niet beperkt tot de garanties van verhandelbaarheid, geschiktheid voor een bepaald doel en niet-inbreukmakendheid. In geen enkel geval zullen de auteurs of auteursrechthouders aansprakelijk zijn voor enige claim, schade of andere aansprakelijkheid, ofwel in een contractshandeling, euveldaad of anderzins, voorkomende van, uit, of in connectie met deze app of het gebruik of andere handelingen in deze app', + /*TODO*///'This app must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This app must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This app utilizes code and assets from the following sources.' => 'Deze app gebuikt code en eigendommen van de volgende bronnen.', + 'This app will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Deze app zal buiten gebruik zijn voor gepland onderhoud aanvangend op %1$d om %2$t.', + 'This app won\'t run when it\'s embedded in a site.' => 'Deze app zal niet werken wanneer deze is ingebed in een site.', + 'This extension injects an API into every website you visit making it possible for those websites to interact with MWC Wallet.' => 'Deze extentie injecteert een API in elke website die u bezoekt, hetgeen het mogelijk maakt voor deze websites om te interacteren met de MWC portemonnee.', + 'This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.' => 'Deze extentie is verstrekt -zoals deze is-, zonder garantie van enige soort, verwoord of geïmpliceerd, inclusief, maar niet beperkt tot de garanties van verhandelbaarheid, geschiktheid voor een bepaald doel en niet-inbreukmakendheid. In geen enkel geval zullen de auteurs of auteursrechthouders aansprakelijk zijn voor enige claim, schade of andere aansprakelijkheid, ofwel in een contractshandeling, euveldaad of anderzins, voorkomende van, uit, of in connectie met deze extentie of het gebruik of andere handelingen in deze extentie.', + /*TODO*///'This extension must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This extension must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This extension utilizes code and assets from the following sources.' => 'Deze extentie gebuikt code en eigendommen van de volgende bronnen.', + 'This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Deze extentie werkt niet correct indien uw browser geconfigureerd is om cookies van derden te blokkeren. Zorg ervoor dat uw browser geconfigureerd is om cookies van derden toe te staan voordat u verdergaat.', + 'This extension won\'t run when it\'s embedded in a site.' => 'Deze extentie werkt niet indien deze is ingebed in een site.', + 'This may take several minutes to complete. The recipient must be online and listening at that address to receive this payment.' => 'Het kan enkele minuten duren om af te ronden. De ontvanger moet online zijn en luisteren op het betreffende adres om de betaling te onvangen.', + 'This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.' => 'Deze wachtwoordzin maakt het mogelijk om uw Portemonnee %1$s te herstellen. Het wordt u aangeraden deze wachtwoordzin te noteren op een veilige, niet digitale wijze.', + 'This site is currently down for maintenance.' => 'Deze site is momenteel niet beschikbaar wegens onderhoud.', + 'This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.' => 'Deze site is verstrekt -zoals deze is-, zonder garantie van enige soort, verwoord of geïmpliceerd, inclusief, maar niet beperkt tot de garanties van verhandelbaarheid, geschiktheid voor een bepaald doel en niet-inbreukmakendheid. In geen enkel geval zullen de auteurs of auteursrechthouders aansprakelijk zijn voor enige claim, schade of andere aansprakelijkheid, ofwel in een contractshandeling, euveldaad of anderzins, voorkomende van, uit, of in connectie met deze site of het gebruik of andere handelingen in deze site.', + /*TODO*///'This site must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This site must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This site uses cookies that are essential to its functionality. By using this site\'s services or clicking OK, you agree to this site\'s use of cookies.' => 'Deze site gebuikt cookies die essentieel zijn voor de functionaliteit. Door gebruik te maken van de services van deze site of door het aanklikken van OK, stemt u in met het gebruik van cookies door deze site.', + 'This site utilizes code and assets from the following sources.' => 'Deze site gebruikt code en eigendommen van de volgende bronnen.', + 'This site will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Deze site zal buiten gebruik zijn wegens gepland onderhoud aanvangend op %1$d om %2$t.', + 'This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.' => 'Deze site zal niet juist functioneren indien u private of incognito browsing modus heeft geactiveerd of indien uw browser zo geconfigureerd is om automatisch cookies en site data te verwijderen. Zorg ervoor dat private en incognito browsing modus uitgeschakeld zijn en dat uw browser geconfigureerd is om cookies en site data te bewaren voordat u verdergaat.', + 'This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Deze site zal niet juist functioneren indien uw browser geconfigureerd is om cookies van derden te blokkeren. Zorg ervoor dat uw browser zo geconfigureerd is dat het cookies van derden toestaat voordat u verdergaat.', + 'This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.' => 'Deze site zal niet werken indien deze is ingebed in een site. Bezoek %1$l om verder te gaan.', + 'This wallet can only be recovered by using its passphrase once it\'s been deleted.' => 'Deze portemonnee kan alleen hersteld worden door gebruik te maken van bijbehorende wachtwoordzin indien deze eenmaal verwijderd is.', + /*TODO*/'This wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Deze portemonnee kan alleen hersteld worden door gebruik te maken van bijbehorende wachtwoordzin indien deze eenmaal verwijderd is.', + /*TODO*///'This wallet is available free of charge and should not be sold in any format. If you paid to access this wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back.' => '', + 'Time to live cut off height must be greater than or equal to the lock height.' => 'Time to live afkap-hoogte moet groter of gelijk zijn aan de versleutelde hoogte.', + 'Time to live cut off height must be greater than the current height.' => 'Time to live afkap-hoogte moet groter of gelijk zijn aan de huidige hoogte.', + 'Tor Proxy Settings' => 'Tor Proxy Instellingen', + 'Transaction %1$s' => 'Transactie %1$s', + 'Transaction Canceled' => 'Transactie Geannuleerd', + 'Transaction Error' => 'Transactie Fout', + 'Transaction Rebroadcast' => 'Transactie Opnieuw Uitzenden', + 'Transactions' => 'Transacties', + 'Transactions Navigation Error' => 'Transacties Navigatie Fout', + 'Translation Contributors' => 'Bijdragers Vertaling', + /*TODO*///'Trezor' => '', + /*TODO*///'Trezor Model One' => '', + /*TODO*///'Trezor Model T' => '', + /*TODO*///'Trezor Safe 3' => '', + /*TODO*///'Trezor Safe 5' => '', + 'Trying to connect to the listener at %1$y.' => 'Proberen verbinding tot stand te brengen met de luisteraar op %1$y.', + 'Trying to connect to the node at %1$y.' => 'Proberen verbinding to stand te brengen met de node op %1$y.', + 'Trying to connect to the prices server at %1$y.' => 'Proberen verbinding to stand te brengen met de prijzen server op %1$y.', + 'Type:' => 'Type:', + 'Unauthorized' => 'Niet geautoriseerd', + 'Unauthorized response from the recipient.' => 'Niet geautoriseerde reactie van de ontvanger.', + 'Unbroadcast transactions can\'t be rebroadcast.' => 'Niet uitgezonden transacties kunnen niet opnieuw uitgezonden worden.', + 'Unconfirmed' => 'Onbevestigd', + 'Unconfirmed amount:' => 'Onbevestigde hoeveelheid:', + 'Unknown' => 'Onbekend', + 'Unlock' => 'Ontgrendelen', + 'Unlocked.' => 'Ontgrendeld.', + 'Unlock Error' => 'Ontgrendel Fout', + /*TODO*///'Unlock that hardware wallet to continue connecting to it.' => '', + 'Unlock the hardware wallet for %1$y to continue getting the payment proof address.' => 'Ontgrendel de hardware portemonnee voor %1$y om door te gaan met het verkrijgen van het betalingsbewijs adres.', + 'Unlock the hardware wallet for %1$y to continue mining.' => 'Ontgrendel de hardware portemonnee voor %1$y om door te gaan met mijnen.', + 'Unlock the hardware wallet for %1$y to continue receiving a payment.' => 'Ontgrendel de hardware portemonnee voor %1$y om door te gaan met het onvangen van een betaling.', + 'Unlock the hardware wallet for %1$y to continue sending the payment.' => 'Ontgrendel de hardware portemonnee voor %1$y om door te gaan met het verzenden van een betaling.', + 'Unlock the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Ontgrendel de hardware portemonnee voor Portemonnee %1$s om door te gaan met het verkrijgen van het betalingsbewijs adres.', + 'Unlock the hardware wallet for Wallet %1$s to continue mining.' => 'Ontgrendel de hardware portemonnee voor Portemonnee %1$s om door te gaan met mijnen.', + 'Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Ontgrendel de hardware portemonnee voor Portemonnee %1$s om door te gaan met het ontvangen van een betaling.', + 'Unlock the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Ontgrendel de hardware portemonnee voor Portemonnee %1$s om door te gaan met het verzenden van een betaling.', + 'Unsupported response from the recipient.' => 'Niet ondersteunde reactie van de ontvanger.', + 'Up' => 'Beschikbaar,', + 'Update Available' => 'Update Beschikbaar', + 'Update Installed' => 'Update Geïnstalleerd', + 'Update your browser to use this feature.' => 'Update uw browser om deze functie te gebruiken.', + 'Updating the prices failed.' => 'Updaten van de prijzen mislukt.', + 'URL' => 'URL', + 'Use custom listener' => 'Gebruik aangepaste luisteraar', + 'Use custom node' => 'Gebruik aangepaste node', + 'Use custom Tor proxy' => 'Gebruik aangepaste Tor proxy', + 'Uses a custom listener instead of the default listener' => 'Gebruikt een aangepaste luisteraar in plaats van de standaard luisteraar', + 'Uses a custom node instead of the default nodes' => 'Gebruikt een aangepaste node in plaats van de standaard nodes', + 'Uses a custom Tor proxy instead of the default Tor proxy' => 'Gebruikt een aangepaste Tor proxy in plaats van de standaard Tor proxy', + 'Utilities' => 'Hulpmiddelen', + 'Value:' => 'Waarde:', + 'Value: %1$c' => 'Waarde: %1$c', + 'Value Copied' => 'Waarde gekopieerd', + 'Value in %1$y' => 'Waarde in %1$y', + 'Verifying the payment proof address failed.' => 'Het verifiëren van het betalingsbewijs adres is mislukt.', + 'Verifying the Slatepack address on the hardware wallet failed.' => 'Het verifiëren van het slatepack adres op de hardware portemonnee is mislukt.', + 'Verifying the Tor address on the hardware wallet failed.' => 'Het verifiëren van het Tor adres op de hardware portemonnee is mislukt.', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no recipient payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the recipient payment proof address displayed matches the following payment proof address.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.' => '', + 'Verify that the node\'s foreign API secret is correct.' => 'Verifieer dat de foreign API secret van de node correct is.', + 'Verify that the pasted address matches the following address when you paste it.' => 'Verifieer dat het gekopieerde adres overeenkomt met het volgende adres wanneer je het kopieert.', + 'Verify that the pasted ID matches the following ID when you paste it.' => 'Verifieer dat de gekopieerde ID overeenkomst met de volgende ID wanneer je het kopieert.', + 'Verify that the pasted value matches the following value when you paste it.' => 'Verifieer dat de gekopieerde waarde overeenkomt met de volgende waarde wanneer je het kopieert.', + 'Verify that the Slatepack address displayed on the hardware wallet matches the following payment proof address.' => 'Verifieer dat het op de hardware portemonnee weergegeven Slatepack adres overeenkomt met het volgende betalingsbewijs adres.', + 'Verify that the Tor address displayed on the hardware wallet matches the following payment proof address.' => 'Verifieer dat het op de hardware portemonnee weergegeven Tor adres overeenkomt met het volgende betalingsbewijs adres.', + 'Verify the payment proof address on the hardware wallet for %1$y to continue getting the payment proof address.' => 'Verifieer het op de hardware portemonnee weergegeven betalingsbewijs adres voor %1$y om door te gaan met het verkrijgen van het betalingsbewijs adres.', + 'Verify the payment proof address on the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Verifieer het op de hardware wallet weergegeven betalingsbewijs adres voor Portemonnee %1$s om door te gaan met het verkrijgen van het betalingsbewijs adres.', + 'Version %1$v' => 'Versie %1$v', + 'Version Changes' => 'Versie Veranderingen', + 'Version Information' => 'Versie Informatie', + 'Version number:' => 'Versienummer:', + 'Wallet %1$s' => 'Portemonnee %1$s', + 'Wallet Error' => 'Portemonnee Fout', + 'Wallet Renamed' => 'Portemonnee Hernoemd', + 'Wallets' => 'Portemonnee', + 'Wallet Settings' => 'Portemonnee Instellingen', + 'Wallet type' => 'Portemonnee type', + 'Wallet type:' => 'Portemonnee type:', + 'Website API integration' => 'Website API integratie', + 'Yes' => 'Ja', + 'You aren\'t connected to a listener.' => 'U bent niet verbonden met een luisteraar.', + /*TODO*///'You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.' => '', + 'You can guarantee that this payment is going to the intended recipient by having the recipient confirm that this payment proof address is their payment proof address.' => 'U kunt garanderen dat deze betaling naar de bedoelde ontvanger gaat door de ontvanger te laten bevestigen dat dit betalingsbewijsadres hun betalingsbewijs adres is.', + 'You can only receive payments at this address while you\'re online and connected to a listener.' => 'U kunt alleen betalingen ontvangen op dit adres wanneer u online bent en verbonden met een luisteraar.', + /*TODO*///'You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.' => '', + 'You can\'t guarantee that this payment is going to the intended recipient since the transaction doesn\'t have a payment proof.' => 'U kunt niet garanderen dat deze betaling naar de bedoelde ontvanger gaat aangezien deze transactie geen betalingsbewijs heeft.', + /*TODO*///'You\'ll be sending %1$c from %2$y for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from %2$y to the following address for a fee of %3$c.' => 'U zult %1$c versturen vanuit %2$y naar het volgende adres voor een vergoeding van %3$c.', + /*TODO*///'You\'ll be sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'U zult %1$c zenden vanuit Portemonnee %2$s naar het volgende adres voor een vergoeding van %3$c.', + 'You\'ll need to provide a Tor proxy address to connect to the node.' => 'U dient een Tor proxy adres te verstrekken om verbinding met de node te maken.', + 'You\'ll need to provide a Tor proxy address to connect to the recipient.' => 'U dient een Tor proxy adres te verstrekken om verbinding met de ontvanger te maken.', + 'You\'ll no longer be able to receive payments at the wallet\'s current address once its address suffix has been changed.' => 'U zal niet langer in staat zijn om betalingen te ontvangen op het huidige portemonnee adres indien het adres achtervoegsel gewijzigd is.', + 'You may need to be already paired with the device before this app can connect to it.' => 'Mogelijkerwijs dient u reeds ge-paired, gekoppeld, te zijn met het apparaat voordat de app er verbinding mee kan maken.', + 'You may need to be already paired with the device before this extension can connect to it.' => 'Mogelijkerwijs dient u reeds ge-paired, gekoppeld, te zijn met het apparaat voordat de extentie er verbinding mee kan maken.', + 'You may need to be already paired with the device before this site can connect to it.' => 'Mogelijkerwijs dient u reeds ge-paired, gekoppeld te zijn met het apparaat voordat de site er verbinding mee kan maken.', + /*TODO*///'You may need to disconnect and reconnect the hardware wallet to connect to it.' => '', + 'You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.' => 'Mogelijkerwijs dient u deze extentie in een tabblad of een window te openen indien het niet mogelijk is om verbinding te maken met een hardware portemonnee.', + 'You may need to specify a different Tor proxy address to connect to the node.' => 'Mogelijkerwijs dient u een ander Tor proxy adres te specificeren om verbinding met de node te maken.', + 'You may need to specify a different Tor proxy address to connect to the recipient.' => 'Mogelijkerwijs dient u een ander Tor proxy adres te specificeren om verbinding met de ontvanger te maken.', + 'You may need to specify a listener address that is served over HTTPS to connect to the listener.' => 'Mogelijkerwijs dient u een luisteraar adres te specificeren dat geserveerd wordt over HTTPS om verbinding met de luisteraar te maken.', + 'You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.' => 'Mogelijkerwijs dient u een node adres te specificeren dat geserveerd wordt over HTTPS of als een Onion service om verbinding met de node te maken.', + 'You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.' => 'Mogelijkerwijs dient u een ontvanger adres te specificeren dat geserveerd wordt over HTTPS of als een Onion service om verbinding met de ontvanger de maken.', + 'Your browser doesn\'t allow using a camera.' => 'Uw browser staat het niet toe een camera te gebruiken.', + 'Your browser doesn\'t allow using USB or Bluetooth devices.' => 'Uw browser staat het niet toe om USB of Bluetooth apparaten te gebruiken.', + 'Your browser doesn\'t support JavaScript. Update your browser and/or enable JavaScript to continue.' => 'Uw browser ondersteunt geen JavaScript. Update uw browser en/of activeer Javascript om verder te gaan', + 'Your browser isn\'t compatible. Update your browser to continue.' => 'Uw browser is niet compatibel. Update uw browser om door te gaan.', + 'You\'re no longer connected to a node.' => 'U bent niet langer verbonden met een node.', + 'You\'re no longer connected to the listener.' => 'U bent niet langer verbinden met de luisteraar.', + 'You\'re no longer connected to the node.' => 'U bent niet langer verbonden met de node.', + 'You\'re not authorized to connect to the node.' => 'U bent niet geautoriseerd om te verbinden met de node.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction doesn\'t have a payment proof.' => 'U verstuurt %1$c voor een vergoeding van %2$c, en deze transactie heeft geen betalingsbewijs.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction\'s recipient payment proof address is the following payment proof address.' => 'U verstuurt %1$c voor een vergoeding van %2$c, en het betalingsbewijs adres van de ontvanger is het volgende betalingsbewijs adres.', + 'Your password was successfully changed.' => 'Uw wachtwoord is succesvol gewijzigd.', + 'You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.' => 'U dient deze betaling als niet legitiem te beschouwen totdat deze is bevestigd op de blockchain.', + 'You were sent %1$c to %2$y.' => 'Aan u is gezonden %1$c aan %2$y.', + 'You were sent %1$c to %2$y with a message.' => 'Aan u is gezonden %1$c aan %2$y met een bericht.', + 'You were sent %1$c to Wallet %2$s.' => 'Aan u is gezonden %1$c naar portemonnee %2$s.', + 'You were sent %1$c to Wallet %2$s with a message.' => 'Aan u is gezonden %1$c naar portemonnee %2$s met een bericht.', + 'You won\'t be able to change address suffixes without being connected to a listener.' => 'U zult niet in staat zijn adres achtervoegsels te wijzigen zonder verbonden te zijn met een luisteraar.', + 'You won\'t be able to rebroadcast transactions without being connected to a node.' => 'U zult niet in staat zijn transacties opnieuw uit te zenden zonder verbonden te zijn met een node.', + 'You won\'t be able to receive payments without being connected to a listener.' => 'U zult niet in staat zijn betalingen te ontvangen zonder verbonden te zijn met een luisteraar.', + 'You won\'t be able to send payments without being connected to a node.' => 'U zult niet in staat zijn betalingen te ontvangen zonder verbonden te zijn met een node.', + ] + ]; +?> diff --git a/languages/german.php b/languages/german.php new file mode 100755 index 0000000..402d107 --- /dev/null +++ b/languages/german.php @@ -0,0 +1,734 @@ + [ + "High ko XMR" => "https://twitter.com/@highkoXMR" + ], + + // Constants + "Constants" => [ + + // Language + "Language" => "Deutsch", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/germany.svg", + + // Currency + "Currency" => "EUR", + + // Extension locale code + "Extension Locale Code" => "de", + + // Fallback + "Fallback" => "de" + ], + + // Text + "Text" => [ + '(?<=,) ' => ' ', + '(?<=.) ' => ' ', + '(?<=:) ' => ' ', + ',(?= )' => ',', + ' (%1$c)' => ' (%1$c)', + '%1$d at %2$t' => '%1$d at %2$t', + '#%1$s' => '#%1$s', + '%1$s%%' => '%1$s%%', + '%1$s–%2$s' => '%1$s–%2$s', + '© %1$s–%2$s Nicolas Flamel.' => '© %1$s–%2$s Nicolas Flamel.', + '© %1$s Nicolas Flamel.' => '© %1$s Nicolas Flamel.', + '%1$u:' => '%1$u:', + '%1$x/%2$x/v%3$v' => '%1$x/%2$x/v%3$v', + '%1$y: %2$m' => '%1$y: %2$m', + '%1$y: %2$m, License: %3$m' => '%1$y: %2$m, Lizenz: %3$m', + 'About' => 'über', + 'About Error' => 'über Fehler', + 'Access to your camera was denied.' => 'Der Zugriff auf die Kamera wurde verweigert.', + 'Account' => 'Konto', + 'Account Error' => 'Konto Fehler', + 'Address' => 'Addresse', + 'Address:' => 'Addresse:', + 'Address Copied' => 'Addresse kopiert', + 'A listener address hasn\'t been provided.' => 'Es wurde keine Listener-Adresse angegeben.', + 'All' => 'Alle', + 'All Error' => 'Alle Fehler', + 'Allow changing base fee' => 'Änderung der Grundgebühr zulassen', + 'Allows changing a transaction\'s base fee when sending a payment' => 'Ermöglicht das Ändern der Grundgebühr einer Transaktion beim Senden einer Zahlung', + 'Allows processing mining related API requests' => 'Ermöglicht die Verarbeitung von Mining-bezogenen API-Anforderungen', + 'A MimbleWimble Coin wallet.' => 'Ein MimbleWimble Coin Wallet.', + 'Amount' => 'Menge', + 'Amount:' => 'Menge:', + 'Amount: %1$c' => 'Menge: %1$c', + 'Amount is empty.' => 'Betrag ist leer.', + 'Amount is invalid.' => 'Betrag ist ungültig.', + /*TODO*///'Amount\'s value when recorded:' => '', + 'An error has occurred. This app will automatically reload in a few seconds.' => 'Ein Fehler ist aufgetreten. Diese App wird in wenigen Sekunden automatisch neu geladen.', + 'An error has occurred. This site will automatically reload in a few seconds.' => 'Ein Fehler ist aufgetreten. Diese Seite wird in wenigen Sekunden automatisch neu geladen.', + 'An error occurred while trying to access your camera.' => 'Beim Versuch, auf Ihre Kamera zuzugreifen, ist ein Fehler aufgetreten.', + /*TODO*///'An internal error occurred.' => '', + 'A node address hasn\'t been provided.' => 'Es wurde keine Knotenadresse angegeben.', + 'An operation is currently being performed on the wallet\'s address suffix.' => 'Am Adresssuffix des Wallets wird derzeit eine Operation durchgeführt.', + 'An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.' => 'Ein Update für diese App ist verfügbar. Möchten Sie das Update jetzt installieren? Wenn dies der Fall ist, wird diese App nach der Installation des Updates neu geladen. Wenn nicht, wird das Update beim nächsten Öffnen dieser App installiert.', + 'An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.' => 'Ein Update für diese Website ist verfügbar. Möchten Sie das Update jetzt installieren? Wenn dies der Fall ist, wird diese Website nach der Installation des Updates neu geladen. Wenn nicht, wird das Update beim nächsten Besuch dieser Seite installiert.', + 'API Settings' => 'API Einstellungen', + 'Approve exporting the root public key for the account at index %1$s on that hardware wallet.' => 'Genehmigen Sie den Export des öffentlichen Root-Schlüssels für das Konto bei Index %1$s auf dieser Hardware-Wallet.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue mining.' => 'Genehmigen Sie den Erhalt einer Transaktion auf der Hardware-Wallet für %1$y, um mit dem Mining fortzufahren.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.' => 'Genehmigen Sie den Empfang einer Transaktion auf der Hardware-Wallet für %1$y, um weiterhin Zahlungen zu erhalten.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.' => 'Genehmigen Sie den Empfang einer Transaktion auf der Hardware-Wallet für Wallet %1$s, um das Mining fortzusetzen.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Genehmigen Sie den Empfang einer Transaktion auf der Hardware-Wallet für Wallet %1$s, um weiterhin Zahlungen zu erhalten.', + /*TODO*///'Approve Receiving Payment' => '', + 'Approve sending the transaction on the hardware wallet for %1$y to continue sending the payment.' => 'Genehmigen Sie das Senden der Transaktion auf der Hardware-Wallet für %1$y, um mit dem Senden der Zahlung fortzufahren.', + 'Approve sending the transaction on the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Genehmigen Sie das Senden der Transaktion auf der Hardware-Wallet für Wallet %1$s, um mit dem Senden der Zahlung fortzufahren.', + 'Approving the transaction on the hardware wallet was denied.' => 'Die Genehmigung der Transaktion auf der Hardware Wallet wurde verweigert.', + 'Are you sure you want to cancel transaction %1$s?' => 'Möchten Sie die Transaktion wirklich abbrechen? %1$s?', + 'Are you sure you want to change the address suffix for %1$y?' => 'Möchten Sie das Adresssuffix für wirklich ändern? %1$y?', + 'Are you sure you want to change the address suffix for Wallet %1$s?' => 'Möchten Sie das Adresssuffix für Wallet wirklich ändern? %1$s?', + 'Are you sure you want to delete %1$y?' => 'Sind Sie sicher, dass Sie löschen möchten %1$y?', + 'Are you sure you want to delete all your wallets?' => 'Sind Sie sicher, dass Sie alle Ihre Wallets löschen möchten??', + 'Are you sure you want to delete Wallet %1$s?' => 'Möchten Sie das Wallet wirklich löschen? %1$s?', + 'Are you sure you want to exit? There\'s a remaining transaction.' => 'Sie sind sicher, dass Sie beenden wollen? Es gibt eine verbleibende Transaktion.', + 'Are you sure you want to exit? There\'s remaining transactions.' => 'Sie sind sicher, dass Sie beenden wollen? Es sind noch Transaktionen vorhanden.', + 'Are you sure you want to reset the settings to their default values?' => 'Möchten Sie die Einstellungen wirklich auf die Standardwerte zurücksetzen?', + 'Are you sure you want to resync %1$y?' => 'Möchten Sie wirklich neu synchronisieren? %1$y?', + 'Are you sure you want to resync Wallet %1$s?' => 'Möchten Sie das Wallet wirklich neu synchronisieren? %1$s?', + 'Attributions' => 'Zuschreibungen', + /*TODO*///'Automatically approve receiving payments' => '', + /*TODO*///'Automatically approves all received payments without requiring manual approval' => '', + 'Automatically lock after a set duration of inactivity' => 'Automatisches Sperren nach einer festgelegten Dauer der Inaktivität', + 'Automatically lock when not focused' => 'Automatisch sperren, wenn nicht fokussiert', + 'Automatic lock activated.' => 'Automatische Sperre aktiviert.', + 'A wallet can\'t send payments to itself.' => 'Ein Wallet kann keine Zahlungen an sich selbst senden.', + 'A wallet with the same passphrase already exists in your list of wallets.' => 'Eine Wallet mit derselben Passphrase existiert bereits in Ihrer Wallet-Liste.', + 'A wallet with the same root public key already exists in your list of wallets.' => 'Eine Wallet mit demselben öffentlichen Root-Schlüssel existiert bereits in Ihrer Wallet-Liste.', + 'Back' => 'Zurück', + 'Bad Gateway' => 'Bad Gateway', + 'Balance' => 'Guthaben', + 'Base fee' => 'Grundgebühr', + 'Base fee is empty.' => 'Die Grundgebühr ist leer.', + 'Base fee is invalid.' => 'Die Grundgebühr ist ungültig.', + /*TODO*///'Bitcoin:' => '', + 'Broadcasting the transaction failed.' => 'Übertragung der Transaktion fehlgeschlagen.', + 'Broadcasting the transaction failed for the following reason.' => 'Die Übertragung der Transaktion ist aus folgendem Grund fehlgeschlagen.', + 'Broadcast transactions can\'t be canceled.' => 'Broadcast-Transaktionen können nicht storniert werden.', + 'Cancel' => 'Stornieren', + 'Canceled' => 'Storniert', + 'Canceled transactions can\'t be rebroadcast.' => 'Stornierte Transaktionen können nicht erneut übertragen werden.', + /*TODO*///'Cancel Error' => '', + 'Change Address Suffix' => 'Adresssuffix ändern', + 'Change Address Suffix Error' => 'Fehler beim Ändern des Adresssuffix', + 'Changed %1$y setting.' => 'Einstellung %1$y geändert.', + 'Changed %1$y setting to %2$y.' => 'Einstellung %1$y geändert zu %2$y.', + 'Changed password.' => 'Passwort geändert.', + 'Change Password' => 'Passwort ändern', + 'Change Password Error' => 'Fehler beim Ändern des Kennworts', + 'Changing the address suffix failed.' => 'Ändern des Adresssuffix fehlgeschlagen.', + 'Changing the address suffix timed out.' => 'Zeitüberschreitung beim Ändern des Adresssuffix.', + 'Changing your password failed.' => 'Das Ändern Ihres Passworts ist fehlgeschlagen.', + 'coinbase' => 'coinbase', + 'Coinbase' => 'Coinbase', + 'Coinbase transactions can\'t be rebroadcast.' => 'Coinbase Transaktionen können nicht erneut gesendet werden.', + /*TODO*///'Coinbase transactions don\'t have a file response.' => '', + 'Compatibility Error' => 'Kompatibilitätsfehler', + 'Confirmed' => 'Bestätigt', + 'Confirmed amount:' => 'Bestätigter Betrag:', + 'Confirmed and unconfirmed' => 'Bestätigt und unbestätigt', + 'Confirmed height:' => 'Bestätigte Höhe:', + 'Confirmed transactions can\'t be canceled.' => 'Bestätigte Transaktionen können nicht storniert werden.', + 'Confirmed transactions can\'t be rebroadcast.' => 'Bestätigte Transaktionen können nicht erneut übertragen werden.', + 'Confirm new password' => 'Bestätige neues Passwort', + 'Confirm new password is empty.' => 'Neues Passwort Bestätigen ist leer.', + 'Confirm Password' => 'Bestätige das Passwort', + 'Confirm password is empty.' => 'Passwort bestätigen ist leer.', + 'Confirm Payment Details' => 'Zahlungsdetails bestätigen', + 'Connect' => 'Verbinden', + 'Connecting to a hardware wallet.' => 'Verbinden mit einer Hardware-Wallet.', + 'Connecting to a node failed.' => 'Verbindung zu einer Node fehlgeschlagen.', + 'Connecting to that hardware wallet failed.' => 'Die Verbindung zu dieser Hardware-Wallet ist fehlgeschlagen.', + /*TODO*///'Connecting to the host failed.' => '', + 'Connecting to the listener failed.' => 'Verbindung zum Listener fehlgeschlagen.', + 'Connecting to the node failed.' => 'Verbindung zum Knoten fehlgeschlagen.', + 'Connecting to the prices server failed.' => 'Verbindung zum Preisserver fehlgeschlagen.', + 'Connecting to the recipient failed.' => 'Verbindung zum Empfänger fehlgeschlagen.', + 'Connect the hardware wallet for %1$y to continue getting the payment proof address.' => 'Verbinden Sie die Hardware-Wallet für %1$y, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Connect the hardware wallet for %1$y to continue mining.' => 'Verbinden Sie die Hardware-Wallet für %1$y, um mit dem Mining fortzufahren.', + 'Connect the hardware wallet for %1$y to continue receiving a payment.' => 'Verbinden Sie die Hardware-Wallet für %1$y, um weiterhin Zahlungen zu erhalten.', + 'Connect the hardware wallet for %1$y to continue sending the payment.' => 'Verbinden Sie die Hardware-Wallet für %1$y, um mit dem Senden der Zahlung fortzufahren.', + 'Connect the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Verbinden Sie die Hardware-Wallet für Wallet %1$s, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Connect the hardware wallet for Wallet %1$s to continue mining.' => 'Verbinden Sie die Hardware-Wallet für Wallet %1$s, um mit dem Mining fortzufahren.', + 'Connect the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Verbinden Sie die Hardware-Wallet für Wallet %1$s, um weiterhin Zahlungen zu erhalten.', + 'Connect the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Verbinden Sie die Hardware-Wallet für Wallet %1$s, um mit dem Senden der Zahlung fortzufahren.', + 'Continue' => 'Fortfahren', + 'Copy' => 'Kopieren', + 'Copy Address Error' => 'Adresse Kopieren Fehler', + 'Copy ID Error' => 'Fehler ID kopieren', + 'Copying the address to your clipboard failed.' => 'Das Kopieren der Adresse in Ihre Zwischenablage ist fehlgeschlagen.', + 'Copying the ID to your clipboard failed.' => 'Das Kopieren der ID in Ihre Zwischenablage ist fehlgeschlagen.', + 'Copying the value to your clipboard failed.' => 'Kopieren des Werts in Ihre Zwischenablage fehlgeschlagen.', + 'Copyright' => 'Copyright', + 'Copy Value Error' => 'Fehler beim Kopieren des Werts', + 'Create' => 'Erstellen', + 'Created hardware wallet Wallet %1$s.' => 'Hardware-Wallet Wallet erstelltv%1$s.', + 'Created wallet Wallet %1$s.' => 'Wallet erstellt Wallet %1$s.', + 'Create Error' => 'Fehler erstellen', + 'Create Wallet Error' => 'Wallet erstellen Fehler', + 'Creating slate failed.' => 'Slate konnte nicht erstellt werden.', + 'Creating transaction failed.' => 'Transaktion erstellen fehlgeschlagen.', + 'Creating wallet failed.' => 'Wallet erstellen fehlgeschlagen.', + 'Currency of the displayed prices' => 'Währung der angezeigten Preise', + 'Current password' => 'Aktuelles Passwort', + 'Current password is empty.' => 'Aktuelles Passwort ist leer.', + 'Current password is incorrect.' => 'Das Passwort ist falsch.', + 'Custom listener address' => 'Benutzerdefinierte Listener-Adresse', + 'Custom node address' => 'Benutzerdefinierte Knotenadresse', + 'Custom node foreign API secret' => 'Fremdes API-Geheimnis des benutzerdefinierten Knotens', + 'Custom Tor proxy address' => 'Benutzerdefinierte Tor-Proxy-Adresse', + 'Default Base Fee' => 'Standardgrundgebühr', + 'Delete' => 'Löschen', + 'Delete All Wallets' => 'Lösche alle Wallets', + 'Delete All Wallets Error' => 'Alle Wallets löschen Fehler', + 'Deleted all wallets.' => 'Lösche alle Wallets.', + 'Deleted wallet %1$y.' => 'Lösche Wallet %1$y.', + 'Deleted wallet Wallet %1$s.' => 'Gelöschtes Wallet Wallet %1$s.', + 'Delete Error' => 'Fehler beim löschen', + /*TODO*///'Description' => '', + 'Destination:' => 'Ziel:', + 'Disclaimer' => 'Haftungsausschluss', + 'Disconnected from the listener.' => 'getrennt vom listener.', + 'Disconnected from the node.' => 'getrennt vom node.', + 'Displayed address type' => 'Angezeigter Adresstyp', + 'Displayed amount type' => 'Angezeigter Betragstyp', + 'Display prices' => 'Preise anzeigen', + 'Displays a message when not able to connect to a listener or when disconnected from a listener' => 'Zeigt eine Meldung an, wenn keine Verbindung zu einem Listener hergestellt werden kann oder wenn die Verbindung zu einem Listener getrennt wird', + 'Displays a message when not able to connect to a node, when a connected node is incompatible, or when disconnected from a node' => 'Zeigt eine Meldung an, wenn keine Verbindung zur Node hergestellt wird, wenn eine Node nicht kompatibel ist oder wenn die Verbindung zur Node getrennt wird', + 'Displays prices for amounts' => 'Zeigt Preise für Beträge an', + /*TODO*///'Donate' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this app.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this extension.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this site.' => '', + 'Don\'t disclose this passphrase to anyone.' => 'Geben Sie diese Passphrase an niemanden weiter.', + 'Down' => 'Down', + /*TODO*///'Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*///'Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*/'Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Jedes Wallet kann nur wiederhergestellt werden, indem ihre Passphrase verwendet wird, nachdem sie gelöscht wurde.', + 'Enable mining API' => 'Aktivieren Sie die Mining-API', + 'Enter a new name for %1$y.' => 'Geben Sie einen neuen Namen für %1$y ein.', + 'Enter a new name for Wallet %1$s.' => 'Geben Sie einen neuen Namen für Wallet %1$s ein.', + 'Enter a wallet\'s passphrase to recover it.' => 'Geben Sie die Passphrase einer Wallet ein, um sie wiederherzustellen.', + 'Enter your password to continue sending the payment.' => 'Geben Sie Ihr Passwort ein, um mit dem Senden der Zahlung fortzufahren.', + 'Enter your password to get the passphrase for %1$y.' => 'Geben Sie Ihr Passwort ein, um die Passphrase für %1$y zu erhalten.', + 'Enter your password to get the passphrase for Wallet %1$s.' => 'Geben Sie Ihr Passwort ein, um die Passphrase für Wallet zu erhalten %1$s.', + /*TODO*///'Enter your pin as the following alphabetic characters to unlock the hardware wallet.' => '', + /*TODO*///'Epic Cash' => '', + /*TODO*///'Epic Cash:' => '', + 'Error' => 'Fehler', + 'Error %1$s' => 'Fehler %1$s', + 'Error response from the recipient.' => 'Fehlerantwort vom Empfänger.', + 'Expired' => 'Abgelaufen', + 'Expired transactions can\'t be canceled.' => 'Abgelaufene Transaktionen können nicht storniert werden.', + 'Expired transactions can\'t be rebroadcast.' => 'Abgelaufene Transaktionen können nicht erneut übertragen werden.', + 'Expire height:' => 'height abgelaufen:', + 'Exporting the root public key on that %1$x hardware wallet was denied.' => 'Das Exportieren des öffentlichen Root-Schlüssels auf dieser Hardware-Wallet %1$x wurde verweigert.', + 'Failed to determine the primary instance.' => 'Fehler beim Ermitteln der primären Instanz.', + 'Failed to initialize dependencies.' => 'Abhängigkeiten konnten nicht initialisiert werden.', + 'Failed to install or update the service worker.' => 'Der Service Worker konnte nicht installiert oder aktualisiert werden.', + 'Failed to load resources. Refresh this site to try again.' => 'Ressourcen konnten nicht geladen werden. Aktualisieren Sie diese Website, um es erneut zu versuchen.', + 'Failed to load resources. Restart this app to try again.' => 'Ressourcen konnten nicht geladen werden. Starten Sie diese App neu, um es erneut zu versuchen.', + 'Fee:' => 'Gebühr:', + /*TODO*///'Fee\'s value when recorded:' => '', + 'Finalize' => 'Abschließen', + 'Finalize the transaction for %1$y to continue sending the payment.' => 'Schließen Sie die Transaktion für %1$y ab, um mit dem Senden der Zahlung fortzufahren.', + 'Finalize the transaction for Wallet %1$s to continue sending the payment.' => 'Schließen Sie die Transaktion für Wallet %1$s ab, um mit dem Senden der Zahlung fortzufahren.', + 'Finalizing the slate failed.' => 'Das Finalisieren von Slate ist fehlgeschlagen.', + 'First' => 'Erste', + 'Floonet' => 'Floonet', + 'Floonet/testnet' => 'Floonet/testnet', + 'Forbidden' => 'Verboten', + 'Forbidden response from the recipient.' => 'Verbotene Antwort des Empfängers.', + 'Forward' => 'Forwärts', + 'From wallet' => 'Von wallet', + 'Gateway Timeout' => 'Gateway-Zeitüberschreitung', + /*TODO*///'Get File Response' => '', + /*TODO*///'Get File Response Error' => '', + 'Get Passphrase' => 'Passphrase erhalten', + 'Get Passphrase Error' => 'Passphrase-Fehler erhalten', + 'Get Payment Proof Address' => 'Erhalten Sie eine Zahlungsnachweisadresse', + 'Get Payment Proof Address Error' => 'Erhalten Sie einen Zahlungsnachweis Adressfehler', + 'Getting the app information from that %1$x hardware wallet failed.' => 'Das Abrufen der App-Informationen von dieser Hardware-Wallet %1$x ist fehlgeschlagen.', + /*TODO*///'Getting the features from that %1$x hardware wallet failed.' => '', + 'Getting the payment proof address failed.' => 'Das Abrufen der Zahlungsnachweisadresse ist fehlgeschlagen.', + 'Getting the root public key from that %1$x hardware wallet failed.' => 'Das Abrufen des öffentlichen Root-Schlüssels von dieser Hardware-Wallet %1$x ist fehlgeschlagen.', + 'Getting the seed cookie from that %1$x hardware wallet failed.' => 'Das Abrufen des Seed-Cookies von dieser Hardware-Wallet %1$x ist fehlgeschlagen.', + /*TODO*///'Give the %1$m file to the payment\'s recipient and open their response file to continue sending the payment.' => '', + /*TODO*///'Give the %1$m file to the payment\'s sender for them to finalize the transaction.' => '', + 'Grin' => 'Grin', + 'Grin:' => 'Grin:', + 'Hardware' => 'Hardware', + 'Hardware wallet' => 'Hardware wallet', + 'Hardware Wallet' => 'Hardware Wallet', + 'Hardware Wallet Approval Requested' => 'Hardware Wallet Genehmigung beantragt', + 'Hardware wallet connected' => 'Hardware wallet verbunden', + 'Hardware Wallet Disconnected' => 'Hardware Wallet getrennt', + 'Hardware Wallet Error' => 'Hardware Wallet Fehler', + 'Hardware Wallet Locked' => 'Hardware Wallet gesperrt', + 'Hardware wallet support' => 'Hardware wallet support', + 'height locked' => 'height gesperrt', + 'Hide' => 'verstecken', + 'HTTP' => 'HTTP', + 'HTTPS' => 'HTTPS', + 'ID:' => 'ID:', + 'ID Copied' => 'ID kopiert', + /*TODO*///'If someone asked you to copy/paste something here you are being scammed!!!' => '', + /*TODO*///'If you paid to access MWC Wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back' => '', + 'Inactive lock activated.' => 'Inaktive Sperre an.', + 'Incorrect password.' => 'Falsches Passwort.', + /*TODO*///'Incorrect pin.' => '', + /*TODO*///'Information' => '', + 'Install Now' => 'Jtzt installieren', + 'Install the MWC Wallet app?' => 'Installieren der MWC Wallet app?', + 'Insufficient balance.' => 'Kein Guthaben.', + 'Interface Settings' => 'Schnittstelleneinstellungen', + 'Internal error' => 'Interner Fehler', + 'Internal Server Error' => 'interner Serverfehler', + 'Invalid amount.' => 'ungültige Menge.', + 'Invalid message.' => 'Ungültige Nachricht.', + /*TODO*///'Invalid message from the host.' => '', + 'Invalid name.' => 'Ungültiger Name.', + 'Invalid origin.' => 'Ungültiger Ursprung.', + 'Invalid parameters' => 'Ungültige Parameter', + 'Invalid passphrase.' => 'Ungültige Passphrase.', + /*TODO*///'Invalid pin.' => '', + 'Invalid recipient address.' => 'Ungültige Empfängeradresse.', + 'Invalid request' => 'Ungültige Anfrage', + 'Invalid request.' => 'Ungültige Anfrage.', + 'Invalid request response from the recipient.' => 'Ungültige Anfrageantwort vom Empfänger.', + 'Invalid response from the recipient.' => 'Ungültige Antwort des Empfängers.', + 'Invalid wallet.' => 'Ungültige Geldbörse.', + 'JavaScript Error' => 'JavaScript Fehler', + 'Kernel excess:' => 'Kernel überschuss:', + 'Language' => 'Sprache', + 'Language changed to %1$y.' => 'Sprache geändert zu %1$y.', + 'Language\'s currency' => 'Währung des Landes', + 'Last' => 'Zuletzt', + 'Ledger Flex' => 'Ledger Flex', + 'Ledger Nano S' => 'Ledger Nano S', + 'Ledger Nano S Plus' => 'Ledger Nano S Plus', + 'Ledger Nano X' => 'Ledger Nano X', + 'Ledger Stax' => 'Ledger Stax', + 'Listener' => 'Listener', + 'Listener connected' => 'Listener verbunden', + 'Listener disconnected' => 'Listener getrennt', + 'Listener Error' => 'Listener Fehler', + 'Listener Settings' => 'Listener Einstellung', + 'Listener settings changed. Disconnecting from the listener.' => 'Listener Einstellungen geändert. Trennen vom Zuhörer.', + 'Loading…' => 'Wird geladen…', + 'Loading Error' => 'Ladefehler', + 'Lock' => 'Sperren', + 'Locked.' => 'Gesperrt.', + 'Lock height:' => 'Sperre height:', + 'Lock if not focused' => 'Sperren, wenn nicht fokussiert', + 'Log' => 'Protokoll', + 'Log Error' => 'Protokollfehler', + 'Log initialized.' => 'Protokoll initialisiert.', + /*TODO*///'Login With Wallet' => '', + 'Mainnet' => 'Hauptnetz', + 'Maintenance' => 'Wartung', + 'Make sure that the correct app is open on the hardware wallet.' => 'Stellen Sie sicher, dass die richtige App auf der Hardware Wallet geöffnet ist.', + 'Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.' => 'Stellen Sie sicher, dass die richtige App auf der Hardware Wallet geöffnet ist und dass die Hardware Wallet nicht gesperrt ist.', + /*TODO*///'Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m' => '', + /*TODO*///'Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m' => '', + /*TODO*///'Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m' => '', + /*TODO*///'Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m' => '', + 'Manage your MimbleWimble Coin' => 'Verwalten Sie Ihre MimbleWimble Coins', + 'Match connection' => 'Match Verbindung', + 'Maximum number of messages that the log can display' => 'Maximale Anzahl von Meldungen, die das Protokoll anzeigen kann', + 'Message:' => 'Nachricht:', + 'Method not found' => 'Methode nicht gefunden', + 'MimbleWimble Coin' => 'MimbleWimble Coin', + 'MimbleWimble Coin:' => 'MimbleWimble Coin:', + 'Minutes between price updates' => 'Minuten zwischen Preisaktualisierungen', + 'Minutes of inactivity required for automatic lock' => 'Minuten der Inaktivität für die automatische Sperre erforderlich', + 'MWC Wallet' => 'MWC Wallet', + 'MWC Wallet — Error' => 'MWC Wallet — Fehler', + 'MWC Wallet — Error %1$s' => 'MWC Wallet — Fehler %1$s', + 'MWC Wallet is an extension that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC Wallet ist eine Erweiterung, mit der Sie Ihre MimbleWimble Coin in Ihrem Webbrowser verwalten können.', + /*TODO*///'MWC Wallet is an open-source, self-custodial wallet that allows you to easily send and receive MimbleWimble Coin. Designed with ease of use and accessibility in mind, this wallet runs on everything from smartwatches to mainframes while providing an intuitive interface that makes using it a breeze.' => '', + 'MWC Wallet is a self-custodial web wallet that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC Wallet ist eine selbstverwahrte Web-Wallet, mit der Sie Ihre MimbleWimble Coin in Ihrem Webbrowser verwalten können.', + 'MWC Wallet — Maintenance' => 'MWC Wallet — Wartung', + 'N/A' => 'N/A', + 'Name' => 'Name', + 'Network changed. Disconnecting from the listener.' => 'Netzwerk geändert. Trennen vom Zuhörer.', + 'Network type' => 'Netzwerk type', + 'Network type:' => 'Netzwerk type:', + 'New password' => 'Neies Passwort', + 'New password is empty.' => 'Neues Passwort ist leer.', + 'New passwords don\'t match.' => 'Neue Passwörter stimmen nicht überein.', + 'New Wallet Passphrase' => 'Neue Wallet-Passphrase', + 'Next' => 'Nächste', + 'Nicolas Flamel' => 'Nicolas Flamel', + 'No' => 'nein', + 'No camera was found. Connect a camera to use this feature.' => 'Es wurde keine Kamera gefunden. Schließen Sie eine Kamera an, um diese Funktion zu verwenden.', + 'Node' => 'Node', + 'Node connected' => 'Node verbunden', + 'Node disconnected' => 'Node getrennt', + 'Node Error' => 'Node Fehler', + 'Node Settings' => 'Node Einstellung', + 'Node settings changed. Disconnecting from the node.' => 'Node Einstellungen geändert. Trennen von der node.', + 'Node warning' => 'Node Warnung', + 'No hardware wallet was selected.' => 'kein hardware wallet wurde ausgewählt.', + 'Non-hardware wallet' => 'kein-hardware wallet', + 'no recent duplicate' => 'kein aktuelles Duplikat', + 'Not compatible with node versions less than %1$v.' => 'Nicht kompatibel mit Node Version kleiner als %1$v.', + 'Not Found' => 'Nicht gefunden', + 'Not found response from the recipient.' => 'Antwort des Empfängers nicht gefunden.', + 'No transactions exist for this wallet.' => 'Für dieses Wallet sind keine Transaktionen vorhanden.', + 'Number of confirmations:' => 'Anzahl der Bestätigungen:', + 'Number of confirmations required to spend an output' => 'Anzahl der Bestätigungen, die erforderlich sind, um eine Ausgabe auszugeben', + 'OK' => 'OK', + 'Onion Service' => 'Onion Service', + 'Only one instance of this app can be open at once. Close all other instances to continue.' => 'Es kann immer nur eine Instanz dieser App geöffnet sein. Schließen Sie alle anderen Instanzen, um fortzufahren.', + 'Only one instance of this extension can be open at once. Close all other instances to continue.' => 'Es kann immer nur eine Instanz dieser Erweiterung geöffnet sein. Schließen Sie alle anderen Instanzen, um fortzufahren.', + 'Only one instance of this site can be open at once. Close all other instances to continue.' => 'Es kann immer nur eine Instanz dieser Seite geöffnet sein. Schließen Sie alle anderen Instanzen, um fortzufahren.', + /*TODO*///'Opening that file failed.' => '', + 'Open in New Tab' => 'In neuem Tab öffnen', + 'Open in New Window' => 'In einem neuen Fenster öffnen', + /*TODO*///'Open Response File' => '', + 'Optional message' => 'Optionale Nachricht', + 'Order Error' => 'Bestellfehler', + 'Output commitment:' => 'Ausgangs-Engagement:', + 'Passphrase' => 'Passphrase', + 'Passphrases can\'t be retrieved from hardware wallets.' => 'Passphrases können nicht aus Hardware Wallets abgerufen werden.', + 'Password' => 'Passwort', + 'Password Changed' => 'Passwort geändert', + 'Password is empty.' => 'Password ist leer.', + 'Passwords don\'t match.' => 'Passwords stimmen nicht überein.', + 'Payload too large response from the recipient.' => 'Payload zu große Antwort des Empfängers.', + 'Payment Details' => 'Zahlungs- Details', + 'Payment Proof' => 'Zahlungs- Nachweis', + 'Payment Proof Address' => 'Zahlungs- Nachweis Adresse', + 'Payment Received' => 'Zahlung empfangen', + /*TODO*///'Pin' => '', + 'plain' => 'flach', + 'Previous' => 'Vorherige', + 'Private Browsing And Site Data Information' => 'Private Browsing- und Site-Dateninformationen', + /*TODO*///'Rebroadcast' => '', + /*TODO*///'Rebroadcast Error' => '', + 'Rebroadcasting the transaction failed.' => 'Die erneute Übertragung der Transaktion ist fehlgeschlagen.', + 'Rebroadcasting the transaction failed for the following reason.' => 'Die erneute Übertragung der Transaktion ist aus folgendem Grund fehlgeschlagen.', + 'Received' => 'Empfangen', + 'Received an invalid response from the node.' => 'Ungültige Antwort von der Node erhalten.', + 'Received an invalid response from the prices server.' => 'Ungültige Antwort vom Preisserver erhalten.', + 'Received transactions can\'t be rebroadcast.' => 'Empfangene Transaktionen können nicht erneut übertragen werden.', + /*TODO*///'Receive Payment As File' => '', + /*TODO*///'Receive Payment As File Error' => '', + 'Recipient address' => 'Empfängeradresse', + 'Recipient address is empty.' => 'Empfängeradresse ist leer.', + 'Recipient address is invalid.' => 'Empfängeradresse ist ungültig.', + 'Recipient address isn\'t supported.' => 'Empfängeradresse wird nicht unterstützt.', + 'Recipient doesn\'t support any available slate versions.' => 'Der Empfänger unterstützt keine verfügbaren Slate-Versionen.', + 'Recipient doesn\'t support payment proofs.' => 'Der Empfänger unterstützt keine Zahlungsnachweise.', + 'Recipient payment proof address:' => 'Zahlungsnachweisadresse des Empfängers:', + 'Recipient payment proof signature:' => 'Unterschrift des Zahlungsnachweises des Empfängers:', + 'Recipient\'s foreign API version isn\'t supported.' => 'Die fremde API-Version des Empfängers wird nicht unterstützt.', + 'Recipient\'s slate versions aren\'t supported.' => 'Slate-Versionen des Empfängers werden nicht unterstützt.', + 'Recorded time:' => 'Aufnahme Zeit:', + 'Recover' => 'Wiederherstellen', + 'Recovered wallet Wallet %1$s.' => 'Geldbörse wiederhergestellt %1$s.', + 'Recover Wallet' => 'Wiederherstellen der Wallet', + 'Recover Wallet Error' => 'Wiederherstellen der Wallet Fehler', + 'Refresh this site to try again.' => 'Aktualisieren Sie diese Website, um es erneut zu versuchen.', + 'Relative height is invalid.' => 'Die relative Höhe ist ungültig.', + 'Release date:' => 'Veröffentlichungsdatum:', + /*TODO*///'Reload Required' => '', + 'Remind me later' => 'Erinner mich später', + 'Rename' => 'Umbenennen', + 'Renamed wallet %1$y to %2$y.' => 'Umbenennen der Wallet %1$y to %2$y.', + 'Renamed wallet Wallet %1$s to %2$y.' => 'Umbenennen des wallet Wallet %1$s to %2$y.', + 'Rename Error' => 'Umbenennen Fehler', + 'Required number of confirmations:' => 'Erforderliche Anzahl an Bestätigungen:', + 'Require payment proof' => 'Zahlungsnachweis verlangen', + 'Requires all new transactions to have a payment proof' => 'Erfordert für alle neuen Transaktionen einen Zahlungsnachweis', + 'Reset settings.' => 'Einstellungen zurücksetzen.', + 'Reset Settings' => 'Einstellungen zurückgesetzt', + 'Reset Settings Error' => 'Einstellungen zurücksetzen Fehler', + 'Restart this app to try again.' => 'Starten Sie diese App neu, um es erneut zu versuchen.', + 'Restart this extension to try again.' => 'Starten Sie diese Erweiterung neu, um es erneut zu versuchen.', + 'Resync' => 'Neu synchronisieren', + 'Resync Error' => 'Neu synchronisieren Fehler', + 'Resyncing can also use a lot of data depending on the number of unspent transaction outputs present in the blockchain.' => 'Die Resynchronisierung kann auch viele Daten verwenden, abhängig von der Anzahl der nicht ausgegebenen Transaktionen, die in der Blockchain vorhanden sind.', + 'Resyncing can take a significant amount of time to complete, and you won\'t be able to send payments from this wallet until it\'s finished resyncing.' => 'Die Neusynchronisierung kann viel Zeit in Anspruch nehmen und Sie können keine Zahlungen von dieser Wallet senden, bis die Neusynchronisierung beendet ist.', + 'Scan QR Code' => 'Scan QR Code', + 'Scan QR Code Error' => 'Scan QR Code Fehler', + 'Selected' => 'Ausgewählt', + 'Select passphrase\'s origin.' => 'Wählen Sie den Ursprung der Passphrase aus.', + 'Send' => 'Senden', + /*TODO*///'Send as file' => '', + 'Sender payment proof address:' => 'Zahlungsnachweisadresse des Absenders:', + 'Send Error' => 'Fehler senden', + /*TODO*///'Sending %1$c from %2$y for a fee of %3$c.' => '', + 'Sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Senden von %1$c von %2$y an die folgende Adresse gegen eine Gebühr von %3$c.', + /*TODO*///'Sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'Sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Senden von %1$c von Wallet %2$s an die folgende Adresse gegen eine Gebühr von %3$c.', + 'Sending Payment' => 'Zahlung senden', + 'Send Payment' => 'Senden Sie die Zahlung', + 'Send Payment Error' => 'Zahlung senden Fehler', + 'Sent' => 'Gesendet', + /*TODO*///'Sent transactions don\'t have a file response.' => '', + /*TODO*///'Set Payment Proof Address Index' => '', + 'Sets the address for the custom listener' => 'Legt die Adresse für den benutzerdefinierten Listener fest', + 'Sets the address for the custom node' => 'Legt die Adresse für den benutzerdefinierten Node fest', + 'Sets the address for the custom Tor proxy' => 'Legt die Adresse für den benutzerdefinierten Tor-Proxy fest', + 'Sets the address type to display for each wallet' => 'Legt den Adresstyp fest, der für jede Brieftasche angezeigt werden soll', + 'Sets the amount type to display in the wallets list' => 'Legt den Betragstyp fest, der in der Brieftaschenliste angezeigt werden soll', + 'Sets the currency of the displayed prices' => 'Legt die Währung der angezeigten Preise fest', + 'Sets the duration of inactivity required for the automatic lock to occur' => 'Legt die Dauer der Inaktivität fest, die für die automatische Sperre erforderlich ist', + 'Sets the duration of time between price updates' => 'Legt die Zeitdauer zwischen Preisaktualisierungen fest', + 'Sets the foreign API secret for the custom node. Leave this empty if the custom node doesn\'t require a foreign API secret' => 'Legt das fremde API-Geheimnis für den benutzerdefinierten Knoten fest. Lassen Sie dieses Feld leer, wenn die Node kein fremdes API-Geheimnis erfordert', + 'Sets the maximum number of messages that the log can display. Earlier messages will be removed once this limit is reached' => 'Legt die maximale Anzahl von Meldungen fest, die das Protokoll anzeigen kann. Frühere Nachrichten werden entfernt, sobald dieses Limit erreicht ist', + /*TODO*///'Sets the network type to use' => '', + 'Sets the number of confirmations required for a new output to be spendable' => 'Legt die Anzahl der Bestätigungen fest, die erforderlich sind, damit eine neue Ausgabe ausgegeben werden kann', + /*TODO*///'Sets the wallet type to use' => '', + 'Settings' => 'Einstellungen', + 'Settings Error' => 'Einstellungen Fehler', + 'Show' => 'Zeige', + 'Show listener connection error messages' => 'Zeige Listener-Verbindungsfehlermeldungen an', + 'Show node connection error messages' => 'Fehlermeldungen zur Node Verbindung anzeigen', + 'Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.' => 'Einige Browser lassen keine Verbindung zu Inhalten zu, die unsicher von einer App bereitgestellt werden, die sicher bereitgestellt wird.', + 'Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.' => 'Einige Browser lassen keine Verbindung zu Inhalten zu, die unsicher von einer Website bereitgestellt werden, die sicher bereitgestellt wird.', + /*TODO*///'Source code:' => '', + 'Spendable' => 'Spendbar', + 'Spendable amount:' => 'Spendbare Anzahl:', + 'Spendable height:' => 'Spendbare height:', + 'Status:' => 'Status:', + 'Successfully connected to the listener.' => 'Erfolgreich mit dem listener verbunden.', + 'Successfully connected to the node.' => 'Erfolgreich mit der Node verbunden.', + 'Successfully connected to the prices server.' => 'Erfolgreich mit dem Preisserver verbunden.', + 'Successfully updated the prices.' => 'SDie Preise wurden erfolgreich aktualisiert.', + 'Synced' => 'Synchronisiert', + 'Syncing' => 'Synchronisieren', + 'Syncing…' => 'Synchronisieren…', + 'Syncing failed' => 'Synchronisierung fehlgeschlagen', + 'Testnet' => 'Testnet', + /*TODO*///'That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.' => '', + /*TODO*///'That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.' => '', + /*TODO*///'That file is invalid, contains unsupported features, or was already used.' => '', + /*TODO*///'That hardware wallet is currently in use.' => '', + 'That hardware wallet isn\'t for %1$y.' => 'Diese Hardware Wallet ist nichts für %1$y.', + 'That hardware wallet isn\'t for Wallet %1$s.' => 'Diese Hardware-Wallet ist nicht für Wallet %1$s.', + /*TODO*///'That hardware wallet was disconnected.' => '', + /*TODO*///'That QR code is invalid.' => '', + 'The address for %1$y was successfully copied to your clipboard.' => 'Die Adresse für %1$y wurde erfolgreich in Ihre Zwischenablage kopiert.', + 'The address for Wallet %1$s was successfully copied to your clipboard.' => 'Die Adresse für Wallet %1$s wurde erfolgreich in Ihre Zwischenablage kopiert.', + /*TODO*///'The address was successfully copied to your clipboard.' => '', + 'The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.' => 'Die App auf dieser Hardware-Wallet %1$x ist nicht kompatibel. Aktualisieren Sie die App auf der Hardware Wallet auf Version %2$v oder neuer, um fortzufahren.', + 'The current height is unknown.' => 'Die aktuelle Höhe ist unbekannt.', + 'The database failed.' => 'Die Datenbank ist fehlgeschlagen.', + 'The fee changed.' => 'Die Gebühr hat sich geändert.', + 'The fee is invalid.' => 'Die Gebühr ist ungültig.', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.' => '', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.' => '', + 'The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.' => 'Die folgenden Personen haben die Übersetzungen für diese App erstellt. Sie können %1$m eine E-Mail senden, wenn Sie daran interessiert sind, diese App in eine andere Sprache zu übersetzen.', + 'The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.' => 'Die folgenden Personen haben die Übersetzungen für diese Erweiterung erstellt. Sie können %1$m eine E-Mail senden, wenn Sie daran interessiert sind, diese Erweiterung in eine andere Sprache zu übersetzen.', + 'The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.' => 'Die folgenden Personen haben die Übersetzungen für diese Seite erstellt. Sie können %1$m eine E-Mail senden, wenn Sie daran interessiert sind, diese Website in eine andere Sprache zu übersetzen.', + 'The hardware wallet is already connected.' => 'Die Hardware Wallet ist bereits verbunden.', + 'The ID for transaction %1$s was successfully copied to your clipboard.' => 'Die ID für Transaktion %1$s wurde erfolgreich in Ihre Zwischenablage kopiert.', + 'The node isn\'t compatible. The node\'s version must be version %1$v or newer.' => 'Der Node ist nicht kompatibel. Die Version der Node muss Version %1$v oder neuer sein.', + 'The node may require a foreign API secret.' => 'Der Node benötigt möglicherweise ein fremdes API-Geheimnis.', + 'The node returned an invalid response.' => 'Der Node hat eine ungültige Antwort zurückgegeben.', + 'The node returned an unauthorized response.' => 'Der Node hat eine nicht autorisierte Antwort zurückgegeben.', + 'The node\'s current height is %1$s.' => 'Die aktuelle Höhe der Node beträgt %1$s.', + 'The node\'s new height is %1$s.' => 'Die Höhe der Node ist %1$s.', + 'The node\'s version is %1$v.' => 'Die Node Version ist %1$v.', + 'The passphrase for %1$y is the following passphrase.' => 'Die Passphrase für %1$y ist die folgende Passphrase.', + 'The passphrase for Wallet %1$s is the following passphrase.' => 'Die Passphrase für Wallet %1$s ist die folgende Passphrase.', + 'The payment proof address for %1$y is the following payment proof address.' => 'Die Zahlungsnachweisadresse für %1$y ist die folgende Zahlungsnachweisadresse.', + 'The payment proof address for Wallet %1$s is the following payment proof address.' => 'Die Zahlungsnachweisadresse für Wallet %1$s ist die folgende Zahlungsnachweisadresse.', + /*TODO*///'The recipient payment proof address you used for the transaction is the following payment proof address.' => '', + 'The recipient responded with the following invalid response.' => 'Der Empfänger hat mit der folgenden ungültigen Antwort geantwortet.', + /*TODO*///'The sender payment proof address you\'re using for the transaction is the following payment proof address.' => '', + 'The setting doesn\'t exist.' => 'Die Einstellung existiert nicht.', + 'The transaction can\'t be rebroadcast.' => 'Die Transaktion kann nicht erneut übertragen werden.', + 'The transaction doesn\'t belong to the wallet.' => 'Die Transaktion gehört nicht zum Wallet.', + 'The transaction doesn\'t exist.' => 'Die Transaktion existiert nicht.', + /*TODO*///'The transaction doesn\'t have a file response or its file response isn\'t known.' => '', + /*TODO*///'The transaction doesn\'t have a payment proof.' => '', + 'The transaction is already canceled.' => 'Die Transaktion ist bereits storniert.', + /*TODO*///'The transaction\'s sender payment proof address is the following payment proof address.' => '', + 'The transaction was successfully canceled.' => 'Die Transaktion wurde erfolgreich abgebrochen.', + 'The transaction was successfully rebroadcast.' => 'Die Transaktion wurde erfolgreich erneut übertragen.', + /*TODO*///'The transaction won\'t have a payment proof.' => '', + 'The update was successfully installed. This app will now reload to use the new version.' => 'Das Update wurde erfolgreich installiert. Diese App wird nun neu geladen, um die neue Version zu verwenden.', + 'The update was successfully installed. This site will now reload to use the new version.' => 'Das Update wurde erfolgreich installiert. Diese Seite wird nun neu geladen, um die neue Version zu verwenden.', + 'The value was successfully copied to your clipboard.' => 'Der Wert wurde erfolgreich in Ihre Zwischenablage kopiert.', + 'The wallet doesn\'t exist.' => 'Dieses Wallet existiert nicht.', + 'The wallet is closed.' => 'Dieses Wallet ist geschlossen.', + 'The wallet isn\'t synced.' => 'Dieses Wallet wird nicht synchronisiert.', + 'The wallet\'s address doesn\'t exist.' => 'Die Adresse des Wallets existiert nicht.', + 'The wallets are locked.' => 'Die Geldbörsen sind gesperrt.', + 'The wallet was successfully renamed.' => 'Die Brieftasche wurde erfolgreich umbenannt.', + 'Third-Party Cookies Information' => 'Informationen zu Cookies von Drittanbietern', + 'This app is currently down for maintenance.' => 'Diese App ist derzeit wegen Wartungsarbeiten nicht verfügbar.', + 'This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.' => 'Diese App wird "wie besehen" bereitgestellt, ohne Gewährleistung jeglicher Art, weder ausdrücklich noch stillschweigend, einschließlich, aber nicht beschränkt auf die Gewährleistung der Marktgängigkeit, Eignung für einen bestimmten Zweck und Nichtverletzung. In keinem Fall haften die Autoren oder Urheberrechtsinhaber für Ansprüche, Schäden oder sonstige Haftung, sei es aus einem Vertrag, aus unerlaubter Handlung oder auf andere Weise, die sich aus, aus oder im Zusammenhang mit dieser App oder der Nutzung oder anderen Handlungen damit ergeben App.', + /*TODO*///'This app must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This app must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This app utilizes code and assets from the following sources.' => 'Diese App verwendet Code und Assets aus den folgenden Quellen.', + 'This app will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Diese App wird wegen planmäßiger Wartungsarbeiten ab dem %1$d um %2$t nicht verfügbar sein.', + 'This app won\'t run when it\'s embedded in a site.' => 'Diese App wird nicht ausgeführt, wenn sie in eine Website eingebettet ist.', + 'This extension injects an API into every website you visit making it possible for those websites to interact with MWC Wallet.' => 'Diese Erweiterung fügt eine API in jede von Ihnen besuchte Website ein, sodass diese Websites mit MWC Wallet interagieren können.', + 'This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.' => 'Diese Erweiterung wird "wie besehen" bereitgestellt, ohne Gewährleistung jeglicher Art, weder ausdrücklich noch stillschweigend, einschließlich, aber nicht beschränkt auf die Gewährleistung der Marktgängigkeit, Eignung für einen bestimmten Zweck und Nichtverletzung. In keinem Fall haften die Autoren oder Urheberrechtsinhaber für Ansprüche, Schäden oder andere Haftungen, sei es aus einem Vertrag, aus unerlaubter Handlung oder auf andere Weise, die sich aus, aus oder im Zusammenhang mit dieser Erweiterung oder der Verwendung oder anderen Handlungen damit ergeben Verlängerung.', + /*TODO*///'This extension must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This extension must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This extension utilizes code and assets from the following sources.' => 'Diese Erweiterung verwendet Code und Assets aus den folgenden Quellen.', + 'This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Diese Erweiterung funktioniert nicht richtig, wenn Ihr Browser so konfiguriert ist, dass Cookies von Drittanbietern blockiert werden. Stellen Sie sicher, dass Ihr Browser so konfiguriert ist, dass er Cookies von Drittanbietern zulässt, bevor Sie fortfahren.', + 'This extension won\'t run when it\'s embedded in a site.' => 'Diese Erweiterung wird nicht ausgeführt, wenn sie in eine Website eingebettet ist.', + 'This may take several minutes to complete. The recipient must be online and listening at that address to receive this payment.' => 'Dies kann einige Minuten dauern. Der Empfänger muss online sein und an dieser Adresse zuhören, um diese Zahlung zu erhalten.', + 'This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.' => 'Mit dieser Passphrase können Sie Wallet %1$s wiederherstellen. Es wird empfohlen, diese Passphrase auf sichere, nicht digitale Weise aufzuzeichnen.', + 'This site is currently down for maintenance.' => 'Diese Website ist derzeit wegen Wartungsarbeiten nicht verfügbar.', + 'This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.' => 'Diese Website wird "wie besehen" bereitgestellt, ohne jegliche ausdrückliche oder stillschweigende Gewährleistung, einschließlich, aber nicht beschränkt auf die Gewährleistung der Marktgängigkeit, Eignung für einen bestimmten Zweck und Nichtverletzung. In keinem Fall haften die Autoren oder Urheberrechtsinhaber für Ansprüche, Schäden oder andere Haftungen, sei es aus einem Vertrag, aus unerlaubter Handlung oder auf andere Weise, die sich aus, aus oder in Verbindung mit dieser Website oder der Nutzung oder anderen Handlungen damit ergeben Grundstück.', + /*TODO*///'This site must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This site must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This site uses cookies that are essential to its functionality. By using this site\'s services or clicking OK, you agree to this site\'s use of cookies.' => 'Diese Website verwendet Cookies, die für ihre Funktionalität unerlässlich sind. Indem Sie die Dienste dieser Website nutzen oder auf OK klicken, stimmen Sie der Verwendung von Cookies durch diese Website zu.', + 'This site utilizes code and assets from the following sources.' => 'Diese Website verwendet Code und Assets aus den folgenden Quellen.', + 'This site will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Diese Website wird wegen planmäßiger Wartungsarbeiten ab dem %1$d um %2$t nicht verfügbar sein.', + 'This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.' => 'Diese Website funktioniert nicht richtig, wenn Sie den privaten oder den Inkognito-Modus aktiviert haben oder wenn Ihr Browser so konfiguriert ist, dass er Cookies und Website-Daten automatisch löscht. Stellen Sie sicher, dass die Modi „Privat“ und „Inkognito“ deaktiviert sind und dass Ihr Browser so konfiguriert ist, dass er Cookies und Website-Daten speichert, bevor Sie fortfahren.', + 'This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Diese Seite funktioniert nicht richtig, wenn Ihr Browser so konfiguriert ist, dass Cookies von Drittanbietern blockiert werden. Stellen Sie sicher, dass Ihr Browser so konfiguriert ist, dass er Cookies von Drittanbietern zulässt, bevor Sie fortfahren.', + 'This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.' => 'Diese Website wird nicht ausgeführt, wenn sie in eine Website eingebettet ist. Besuchen Sie %1$l, um fortzufahren.', + 'This wallet can only be recovered by using its passphrase once it\'s been deleted.' => 'Diese Brieftasche kann nur wiederhergestellt werden, indem ihre Passphrase verwendet wird, nachdem sie gelöscht wurde.', + /*TODO*/'This wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Diese Brieftasche kann nur wiederhergestellt werden, indem ihre Passphrase verwendet wird, nachdem sie gelöscht wurde.', + /*TODO*///'This wallet is available free of charge and should not be sold in any format. If you paid to access this wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back.' => '', + 'Time to live cut off height must be greater than or equal to the lock height.' => 'Die Time-to-Live-Cutoff-Höhe muss größer oder gleich der Sperrhöhe sein.', + 'Time to live cut off height must be greater than the current height.' => 'Die Time-to-Live-Cutoff-Höhe muss größer sein als die aktuelle Höhe.', + 'Tor Proxy Settings' => 'Tor Proxy Einstellung', + 'Transaction %1$s' => 'Transaktion %1$s', + 'Transaction Canceled' => 'Transaktion abgebrochen', + 'Transaction Error' => 'Transaktion Fehler', + 'Transaction Rebroadcast' => 'Transaktion Wiederholung', + 'Transactions' => 'Transaktionen', + 'Transactions Navigation Error' => 'Transaktionsnavigationsfehler', + 'Translation Contributors' => 'Übersetzung Mitwirkende', + /*TODO*///'Trezor' => '', + /*TODO*///'Trezor Model One' => '', + /*TODO*///'Trezor Model T' => '', + /*TODO*///'Trezor Safe 3' => '', + /*TODO*///'Trezor Safe 5' => '', + 'Trying to connect to the listener at %1$y.' => 'Es wird versucht, eine Verbindung zum Listener um %1$y herzustellen.', + 'Trying to connect to the node at %1$y.' => 'Es wird versucht, eine Verbindung zum Knoten bei %1$y herzustellen.', + 'Trying to connect to the prices server at %1$y.' => 'Es wird versucht, eine Verbindung zum Preisserver unter %1$y herzustellen.', + 'Type:' => 'Typ:', + 'Unauthorized' => 'Unbefugt', + 'Unauthorized response from the recipient.' => 'Nicht autorisierte Antwort des Empfängers.', + 'Unbroadcast transactions can\'t be rebroadcast.' => 'Unbroadcast-Transaktionen können nicht erneut übertragen werden.', + 'Unconfirmed' => 'Unbestätigt', + 'Unconfirmed amount:' => 'Unbestätigter Betrag:', + 'Unknown' => 'Unbekannt', + 'Unlock' => 'Freischalten', + 'Unlocked.' => 'Entsperrt.', + 'Unlock Error' => 'Entsperrfehler', + /*TODO*///'Unlock that hardware wallet to continue connecting to it.' => '', + 'Unlock the hardware wallet for %1$y to continue getting the payment proof address.' => 'Entsperren Sie die Hardware-Wallet für %1$y, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Unlock the hardware wallet for %1$y to continue mining.' => 'Entsperren Sie die Hardware-Wallet für %1$y, um mit dem Mining fortzufahren.', + 'Unlock the hardware wallet for %1$y to continue receiving a payment.' => 'Entsperren Sie die Hardware-Wallet für %1$y, um weiterhin Zahlungen zu erhalten.', + 'Unlock the hardware wallet for %1$y to continue sending the payment.' => 'Entsperren Sie die Hardware-Wallet für %1$y, um mit dem Senden der Zahlung fortzufahren.', + 'Unlock the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Entsperren Sie die Hardware-Wallet für Wallet %1$s, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Unlock the hardware wallet for Wallet %1$s to continue mining.' => 'Entsperren Sie die Hardware-Wallet für Wallet %1$s, um mit dem Mining fortzufahren.', + 'Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Entsperren Sie die Hardware-Wallet für Wallet %1$s, um weiterhin Zahlungen zu erhalten.', + 'Unlock the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Entsperren Sie die Hardware-Wallet für Wallet %1$s, um mit dem Senden der Zahlung fortzufahren.', + 'Unsupported response from the recipient.' => 'Nicht unterstützte Antwort vom Empfänger.', + 'Up' => 'Huch', + 'Update Available' => 'Update verfügbar', + 'Update Installed' => 'Aktualisierung installiert', + 'Update your browser to use this feature.' => 'Aktualisieren Sie Ihren Browser, um diese Funktion zu verwenden.', + 'Updating the prices failed.' => 'Die Aktualisierung der Preise ist fehlgeschlagen.', + 'URL' => 'URL', + 'Use custom listener' => 'Verwende Benutzerdefinierten listener', + 'Use custom node' => 'Verwende Benutzerdefinierten node', + 'Use custom Tor proxy' => 'Verwenden Sie einen benutzerdefinierten Tor-Proxy', + 'Uses a custom listener instead of the default listener' => 'Verwendet einen benutzerdefinierten Listener anstelle des Standard-Listeners', + 'Uses a custom node instead of the default nodes' => 'Verwendet einen benutzerdefinierten Knoten anstelle der Standardknoten', + 'Uses a custom Tor proxy instead of the default Tor proxy' => 'Verwendet einen benutzerdefinierten Tor-Proxy anstelle des standardmäßigen Tor-Proxys', + 'Utilities' => 'Dienstprogramme', + 'Value:' => 'Wert:', + 'Value: %1$c' => 'Wert: %1$c', + 'Value Copied' => 'Wert kopiert', + 'Value in %1$y' => 'Wert in %1$y', + 'Verifying the payment proof address failed.' => 'Die Überprüfung der Zahlungsnachweisadresse ist fehlgeschlagen.', + 'Verifying the Slatepack address on the hardware wallet failed.' => 'Die Überprüfung der Slatepack-Adresse auf der Hardware-Wallet ist fehlgeschlagen.', + 'Verifying the Tor address on the hardware wallet failed.' => 'Die Überprüfung der Tor-Adresse auf der Hardware-Wallet ist fehlgeschlagen.', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no recipient payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the recipient payment proof address displayed matches the following payment proof address.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.' => '', + 'Verify that the node\'s foreign API secret is correct.' => 'Stellen Sie sicher, dass das fremde API-Geheimnis des Knotens korrekt ist.', + 'Verify that the pasted address matches the following address when you paste it.' => 'Vergewissern Sie sich beim Einfügen, dass die eingefügte Adresse mit der folgenden Adresse übereinstimmt.', + 'Verify that the pasted ID matches the following ID when you paste it.' => 'Stellen Sie beim Einfügen sicher, dass die eingefügte ID mit der folgenden ID übereinstimmt.', + 'Verify that the pasted value matches the following value when you paste it.' => 'Stellen Sie beim Einfügen sicher, dass der eingefügte Wert mit dem folgenden Wert übereinstimmt.', + 'Verify that the Slatepack address displayed on the hardware wallet matches the following payment proof address.' => 'Überprüfen Sie, ob die auf der Hardware Wallet angezeigte Slatepack-Adresse mit der folgenden Zahlungsnachweisadresse übereinstimmt.', + 'Verify that the Tor address displayed on the hardware wallet matches the following payment proof address.' => 'Überprüfen Sie, ob die auf der Hardware Wallet angezeigte Tor-Adresse mit der folgenden Zahlungsnachweisadresse übereinstimmt.', + 'Verify the payment proof address on the hardware wallet for %1$y to continue getting the payment proof address.' => 'Überprüfen Sie die Zahlungsnachweisadresse auf der Hardware Wallet für %1$y, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Verify the payment proof address on the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Überprüfen Sie die Zahlungsnachweisadresse auf der Hardware Wallet für Wallet %1$s, um weiterhin die Zahlungsnachweisadresse zu erhalten.', + 'Version %1$v' => 'Version %1$v', + 'Version Changes' => 'Version Änderungen', + 'Version Information' => 'Version Information', + 'Version number:' => 'Version Nummer:', + 'Wallet %1$s' => 'Wallet %1$s', + 'Wallet Error' => 'Wallet Fehler', + 'Wallet Renamed' => 'Wallet Umbenannt', + 'Wallets' => 'Wallets', + 'Wallet Settings' => 'Wallet Einstellungen', + 'Wallet type' => 'Wallet Typ', + 'Wallet type:' => 'Wallet Typ:', + 'Website API integration' => 'Website-API-Integration', + 'Yes' => 'Ja', + 'You aren\'t connected to a listener.' => 'Sie sind nicht mit listener verbunden.', + /*TODO*///'You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.' => '', + 'You can guarantee that this payment is going to the intended recipient by having the recipient confirm that this payment proof address is their payment proof address.' => 'Sie können garantieren, dass diese Zahlung an den beabsichtigten Empfänger geht, indem Sie den Empfänger bestätigen lassen, dass diese Zahlungsnachweisadresse seine Zahlungsnachweisadresse ist.', + 'You can only receive payments at this address while you\'re online and connected to a listener.' => 'Unter dieser Adresse können Sie nur dann Zahlungen erhalten, wenn Sie online und mit einem Zuhörer verbunden sind.', + /*TODO*///'You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.' => '', + 'You can\'t guarantee that this payment is going to the intended recipient since the transaction doesn\'t have a payment proof.' => 'Sie können nicht garantieren, dass diese Zahlung an den beabsichtigten Empfänger geht, da für diese Transaktion kein Zahlungsnachweis vorliegt.', + /*TODO*///'You\'ll be sending %1$c from %2$y for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Gegen eine Gebühr von %3$c senden Sie %1$c von %2$y an die folgende Adresse.', + /*TODO*///'You\'ll be sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Sie senden %1$c von Wallet %2$s an die folgende Adresse gegen eine Gebühr von %3$c.', + 'You\'ll need to provide a Tor proxy address to connect to the node.' => 'Sie müssen eine Tor-Proxy-Adresse angeben, um sich mit dem Knoten zu verbinden.', + 'You\'ll need to provide a Tor proxy address to connect to the recipient.' => 'Sie müssen eine Tor-Proxy-Adresse angeben, um sich mit dem Empfänger zu verbinden.', + 'You\'ll no longer be able to receive payments at the wallet\'s current address once its address suffix has been changed.' => 'Sobald das Adresssuffix geändert wurde, können Sie keine Zahlungen mehr an der aktuellen Adresse des Wallets empfangen.', + 'You may need to be already paired with the device before this app can connect to it.' => 'Möglicherweise müssen Sie bereits mit dem Gerät gekoppelt sein, bevor diese App eine Verbindung herstellen kann.', + 'You may need to be already paired with the device before this extension can connect to it.' => 'Möglicherweise müssen Sie bereits mit dem Gerät gekoppelt sein, bevor diese Erweiterung eine Verbindung herstellen kann.', + 'You may need to be already paired with the device before this site can connect to it.' => 'Möglicherweise müssen Sie bereits mit dem Gerät gekoppelt sein, bevor diese Website eine Verbindung herstellen kann.', + /*TODO*///'You may need to disconnect and reconnect the hardware wallet to connect to it.' => '', + 'You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.' => 'Möglicherweise müssen Sie diese Erweiterung in einem Tab oder Fenster öffnen, wenn sie keine Verbindung zu einer Hardware-Wallet herstellen kann.', + 'You may need to specify a different Tor proxy address to connect to the node.' => 'Möglicherweise müssen Sie eine andere Tor-Proxy-Adresse angeben, um sich mit dem Knoten zu verbinden.', + 'You may need to specify a different Tor proxy address to connect to the recipient.' => 'Möglicherweise müssen Sie eine andere Tor-Proxy-Adresse angeben, um sich mit dem Empfänger zu verbinden.', + 'You may need to specify a listener address that is served over HTTPS to connect to the listener.' => 'Möglicherweise müssen Sie eine Listener-Adresse angeben, die über HTTPS bereitgestellt wird, um eine Verbindung zum Listener herzustellen.', + 'You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.' => 'Möglicherweise müssen Sie eine Knotenadresse angeben, die über HTTPS oder als Onion-Dienst bereitgestellt wird, um eine Verbindung zum Knoten herzustellen.', + 'You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.' => 'Möglicherweise müssen Sie eine Empfängeradresse angeben, die über HTTPS oder als Onion-Dienst bereitgestellt wird, um eine Verbindung zum Empfänger herzustellen.', + 'Your browser doesn\'t allow using a camera.' => 'Ihr Browser lässt die Verwendung einer Kamera nicht zu.', + 'Your browser doesn\'t allow using USB or Bluetooth devices.' => 'Ihr Browser lässt die Verwendung von USB- oder Bluetooth-Geräten nicht zu.', + 'Your browser doesn\'t support JavaScript. Update your browser and/or enable JavaScript to continue.' => 'Ihr Browser unterstützt kein JavaScript. Aktualisieren Sie Ihren Browser und/oder aktivieren Sie JavaScript, um fortzufahren.', + 'Your browser isn\'t compatible. Update your browser to continue.' => 'Ihr Browser ist nicht kompatibel. Aktualisieren Sie Ihren Browser, um fortzufahren.', + 'You\'re no longer connected to a node.' => 'Sie sind nicht mehr mit einem Knoten verbunden.', + 'You\'re no longer connected to the listener.' => 'Sie sind nicht mehr mit dem Listener verbunden.', + 'You\'re no longer connected to the node.' => 'Sie sind nicht mehr mit dem Knoten verbunden.', + 'You\'re not authorized to connect to the node.' => 'Sie sind nicht berechtigt, eine Verbindung mit dem Knoten herzustellen.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction doesn\'t have a payment proof.' => 'Sie senden %1$c gegen eine Gebühr von %2$c und für diese Transaktion gibt es keinen Zahlungsnachweis.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction\'s recipient payment proof address is the following payment proof address.' => 'Sie senden %1$c gegen eine Gebühr von %2$c und die Zahlungsnachweisadresse des Empfängers ist die folgende Zahlungsnachweisadresse.', + 'Your password was successfully changed.' => 'Dein Kennwort wurde erfolgreich geändert.', + 'You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.' => 'Sie sollten diese Zahlung nicht als legitim betrachten, bis sie auf der Blockchain bestätigt wurde.', + 'You were sent %1$c to %2$y.' => 'Sie senden %1$c an %2$y.', + 'You were sent %1$c to %2$y with a message.' => 'Sie senden %1$c an %2$y mit einer Nachricht.', + 'You were sent %1$c to Wallet %2$s.' => 'Sie senden %1$c an Wallet %2$s.', + 'You were sent %1$c to Wallet %2$s with a message.' => 'Sie senden %1$c to Wallet %2$s mit der Nachricht.', + 'You won\'t be able to change address suffixes without being connected to a listener.' => 'Sie können Adresssuffixe nicht ändern, ohne mit einem listener verbunden zu sein.', + 'You won\'t be able to rebroadcast transactions without being connected to a node.' => 'Sie können keine Transaktionen erneut übertragen, ohne mit einem Node verbunden zu sein.', + 'You won\'t be able to receive payments without being connected to a listener.' => 'Sie können keine Zahlungen erhalten, ohne mit einem listener verbunden zu sein.', + 'You won\'t be able to send payments without being connected to a node.' => 'Sie können keine Zahlungen senden, ohne mit einem Knoten verbunden zu sein.', + ] + ]; +?> diff --git a/languages/greek.php b/languages/greek.php new file mode 100755 index 0000000..b249cc2 --- /dev/null +++ b/languages/greek.php @@ -0,0 +1,734 @@ + [ + "NickTheG" => "https://twitter.com/@progressevakos" + ], + + // Constants + "Constants" => [ + + // Language + "Language" => "Ελληνικά", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/greece.svg", + + // Currency + "Currency" => "EUR", + + // Extension locale code + "Extension Locale Code" => "el", + + // Fallback + "Fallback" => "el" + ], + + // Text + "Text" => [ + '(?<=,) ' => ' ', + '(?<=.) ' => ' ', + '(?<=:) ' => ' ', + ',(?= )' => ',', + ' (%1$c)' => ' (%1$c)', + '%1$d at %2$t' => '%1$d στις %2$t', + '#%1$s' => '#%1$s', + '%1$s%%' => '%1$s%%', + '%1$s–%2$s' => '%1$s–%2$s', + '© %1$s–%2$s Nicolas Flamel.' => '© %1$s–%2$s Nicolas Flamel.', + '© %1$s Nicolas Flamel.' => '© %1$s Nicolas Flamel.', + '%1$u:' => '%1$u:', + '%1$x/%2$x/v%3$v' => '%1$x/%2$x/v%3$v', + '%1$y: %2$m' => '%1$y: %2$m', + '%1$y: %2$m, License: %3$m' => '%1$y: %2$m, Άδεια: %3$m', + 'About' => 'Πληροφορίες', + 'About Error' => 'Πληροφορίες Σφάλματος', + 'Access to your camera was denied.' => 'Η πρόσβαση στην κάμερά σας απορρίφθηκε.', + 'Account' => 'Λογαριασμός', + 'Account Error' => 'Σφάλμα Λογαριασμού', + 'Address' => 'Διεύθυνση', + 'Address:' => 'Διεύθυνση:', + 'Address Copied' => 'H Διεύθυνση αντιγράφηκε', + 'A listener address hasn\'t been provided.' => 'Δεν έχει δοθεί διεύθυνση ακροατή.', + 'All' => 'Όλα', + 'All Error' => 'Όλα τα σφάλματα', + 'Allow changing base fee' => 'Να επιτρέπονται οι αλλαγές στη βασική χρέωση', + 'Allows changing a transaction\'s base fee when sending a payment' => 'Επιτρέπει την αλλαγή της βασικής τιμής προμήθειας μιας συναλλαγής κατά την αποστολή μιας πληρωμής', + 'Allows processing mining related API requests' => 'Επιτρέπει την επεξεργασία αιτημάτων API σχετικά με την εξόρυξη', + 'A MimbleWimble Coin wallet.' => 'Πορτοφόλι MimbleWimble Coin.', + 'Amount' => 'Ποσό', + 'Amount:' => 'Ποσό:', + 'Amount: %1$c' => 'Ποσό: %1$c', + 'Amount is empty.' => 'Το ποσό είναι κενό.', + 'Amount is invalid.' => 'Το ποσό δεν είναι έγκυρο.', + /*TODO*///'Amount\'s value when recorded:' => '', + 'An error has occurred. This app will automatically reload in a few seconds.' => 'Παρουσιάστηκε ένα σφάλμα. Αυτή η εφαρμογή θα επαναφορτωθεί αυτόματα σε λίγα δευτερόλεπτα.', + 'An error has occurred. This site will automatically reload in a few seconds.' => 'Παρουσιάστηκε ένα σφάλμα. Αυτή η ιστοσελίδα θα επαναφορτωθεί αυτόματα σε λίγα δευτερόλεπτα.', + 'An error occurred while trying to access your camera.' => 'Παρουσιάστηκε σφάλμα κατά την προσπάθεια πρόσβασης στην κάμερά σας.', + /*TODO*///'An internal error occurred.' => '', + 'A node address hasn\'t been provided.' => 'Δεν έχει δοθεί διεύθυνση κόμβου.', + 'An operation is currently being performed on the wallet\'s address suffix.' => 'Μια λειτουργία είναι σε εξέλιξη στο επίθημα διεύθυνσης πορτοφολιού.', + 'An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.' => 'Υπάρχει μια ενημέρωση για αυτή την εφαρμογή. Θέλετε να εγκαταστήσετε την ενημέρωση τώρα; Αν ναι, αυτή η εφαρμογή θα φορτωθεί ξανά μόλις εγκατασταθεί η ενημέρωση. Αν όχι, η ενημέρωση θα εγκατασταθεί την επόμενη φορά που θα ανοίξετε αυτή την εφαρμογή.', + 'An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.' => 'Υπάρχει μια ενημέρωση για αυτή την ιστοσελίδα. Θέλετε να εγκαταστήσετε την ενημέρωση τώρα; Αν ναι, αυτή η ιστοσελίδα θα φορτωθεί ξανά μόλις εγκατασταθεί η ενημέρωση. Αν όχι, η ενημέρωση θα εγκατασταθεί την επόμενη φορά που θα επισκεφτείτε αυτήν την ιστοσελίδα.', + 'API Settings' => 'Ρυθμίσεις API', + 'Approve exporting the root public key for the account at index %1$s on that hardware wallet.' => 'Εγκρίνετε την εξαγωγή του ριζικού δημόσιου κλειδιού για τον λογαριασμό στο ευρετήριο %1$s σε αυτό το πορτοφόλι υλικού.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue mining.' => 'Επιβεβαιώστε την λήψη μιας συναλλαγής στο πορτοφόλι υλικού για %1$y για να συνεχίσετε την εξόρυξη.', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.' => 'Εγκρίνετε τη λήψη μιας συναλλαγής στο πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε πληρωμή.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.' => 'Εγκρίνετε τη λήψη μιας συναλλαγής στο πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε την εξόρυξη.', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Εγκρίνετε τη λήψη μιας συναλλαγής στο πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε πληρωμή.', + /*TODO*///'Approve Receiving Payment' => '', + 'Approve sending the transaction on the hardware wallet for %1$y to continue sending the payment.' => 'Εγκρίνετε την αποστολή της συναλλαγής στο πορτοφόλι υλικού για %1$y για να προχωρήσει στην αποστολή της πληρωμής.', + 'Approve sending the transaction on the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Εγκρίνετε την αποστολή της συναλλαγής στο πορτοφόλι υλικού για το Πορτοφόλι %1$s για να προχωρησει στην πληρωμή.', + 'Approving the transaction on the hardware wallet was denied.' => 'Η έγκριση της συναλλαγής στο πορτοφόλι υλικού απορρίφθηκε.', + 'Are you sure you want to cancel transaction %1$s?' => 'Είστε βέβαιοι ότι θέλετε να ακυρώσετε τη συναλλαγή %1$s;', + 'Are you sure you want to change the address suffix for %1$y?' => 'Είστε βέβαιοι ότι θέλετε να αλλάξετε το επίθημα διεύθυνσης για %1$y;', + 'Are you sure you want to change the address suffix for Wallet %1$s?' => 'Είστε βέβαιοι ότι θέλετε να αλλάξετε το επίθημα διεύθυνσης για το Πορτοφόλι %1$s;', + 'Are you sure you want to delete %1$y?' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε το %1$y;', + 'Are you sure you want to delete all your wallets?' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα πορτοφόλια σας;', + 'Are you sure you want to delete Wallet %1$s?' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε το Πορτοφόλι %1$s;', + 'Are you sure you want to exit? There\'s a remaining transaction.' => 'Είστε σίγουρος ότι θέλετε να φύγετε; Εκκρεμεί μια συναλλαγή.', + 'Are you sure you want to exit? There\'s remaining transactions.' => 'Είστε σίγουρος ότι θέλετε να φύγετε; Εκκρεμούν συναλλαγές.', + 'Are you sure you want to reset the settings to their default values?' => 'Είστε βέβαιοι ότι θέλετε να επαναφέρετε τις ρυθμίσεις στις προεπιλεγμένες τιμές;', + 'Are you sure you want to resync %1$y?' => 'Είστε βέβαιοι ότι θέλετε να επανασυγχρονίσετε το %1$y;', + 'Are you sure you want to resync Wallet %1$s?' => 'Είστε βέβαιοι ότι θέλετε να επανασυγχρονίσετε το Πορτοφόλι %1$s;', + 'Attributions' => 'Αναφορές', + /*TODO*///'Automatically approve receiving payments' => '', + /*TODO*///'Automatically approves all received payments without requiring manual approval' => '', + 'Automatically lock after a set duration of inactivity' => 'Αυτόματο κλείδωμα μετά από μια καθορισμένη διάρκεια αδράνειας', + 'Automatically lock when not focused' => 'Αυτόματο κλείδωμα όταν είναι εκτός εστίασης', + 'Automatic lock activated.' => 'Ενεργοποιήθηκε το αυτόματο κλείδωμα.', + 'A wallet can\'t send payments to itself.' => 'Ένα πορτοφόλι δεν μπορεί να στείλει πληρωμές στον εαυτό του.', + 'A wallet with the same passphrase already exists in your list of wallets.' => 'Ένα πορτοφόλι με την ίδια φράση πρόσβασης υπάρχει ήδη στη λίστα των πορτοφολιών σας.', + 'A wallet with the same root public key already exists in your list of wallets.' => 'Ένα πορτοφόλι με το ίδιο ριζικό δημόσιο κλειδί υπάρχει ήδη στη λίστα των πορτοφολιών σας.', + 'Back' => 'Πίσω', + 'Bad Gateway' => 'Κακή Πύλη', + 'Balance' => 'Υπόλοιπο', + 'Base fee' => 'Βασικό Τέλος Πληρωμής', + 'Base fee is empty.' => 'Το Βασικό Τέλος Πληρωμής είναι κενό.', + 'Base fee is invalid.' => 'Το Βασικό Τέλος Πληρωμής δεν είναι έγκυρο.', + /*TODO*///'Bitcoin:' => '', + 'Broadcasting the transaction failed.' => 'Η μετάδοση της συναλλαγής απέτυχε.', + 'Broadcasting the transaction failed for the following reason.' => 'Η μετάδοση της συναλλαγής απέτυχε για τον ακόλουθο λόγο.', + 'Broadcast transactions can\'t be canceled.' => 'Δεν είναι δυνατή η ακύρωση των συναλλαγών μετάδοσης.', + 'Cancel' => 'Ακύρωση', + 'Canceled' => 'Ακυρώθηκε', + 'Canceled transactions can\'t be rebroadcast.' => 'Οι ακυρωμένες συναλλαγές δεν μπορούν να αναμεταδοθούν.', + /*TODO*///'Cancel Error' => '', + 'Change Address Suffix' => 'Αλλαγή επιθήματος διεύθυνσης', + 'Change Address Suffix Error' => 'Σφάλμα επιθήματος αλλαγής διεύθυνσης', + 'Changed %1$y setting.' => 'Άλλαξε η ρύθμιση %1$y.', + 'Changed %1$y setting to %2$y.' => 'Η ρύθμιση %1$y άλλαξε σε %2$y.', + 'Changed password.' => 'Αλλαγμένος κωδικός πρόσβασης.', + 'Change Password' => 'Αλλάξτε κωδικό', + 'Change Password Error' => 'Σφάλμα αλλαγής κωδικού πρόσβασης', + 'Changing the address suffix failed.' => 'Η αλλαγή του επιθέματος διεύθυνσης απέτυχε.', + 'Changing the address suffix timed out.' => 'Η αλλαγή του επιθέματος διεύθυνσης έληξε.', + 'Changing your password failed.' => 'Η αλλαγή του κωδικού πρόσβασής σας απέτυχε.', + 'coinbase' => 'coinbase', + 'Coinbase' => 'Coinbase', + 'Coinbase transactions can\'t be rebroadcast.' => 'Οι συναλλαγές Coinbase δεν μπορούν να αναμεταδοθούν.', + /*TODO*///'Coinbase transactions don\'t have a file response.' => '', + 'Compatibility Error' => 'Σφάλμα συμβατότητας', + 'Confirmed' => 'Επιβεβαιωμένο', + 'Confirmed amount:' => 'Επιβεβαιωμένο ποσό:', + 'Confirmed and unconfirmed' => 'Επιβεβαιωμένα και Μη Επιβεβαιωμένα', + 'Confirmed height:' => 'Επιβεβαιωμένο ύψος:', + 'Confirmed transactions can\'t be canceled.' => 'Δεν είναι δυνατή η ακύρωση των επιβεβαιωμένων συναλλαγών.', + 'Confirmed transactions can\'t be rebroadcast.' => 'Οι επιβεβαιωμένες συναλλαγές δεν μπορούν να αναμεταδοθούν.', + 'Confirm new password' => 'Επιβεβαίωση Νέου Κωδικού', + 'Confirm new password is empty.' => 'Το πεδίο Επιβεβαίωση κωδικού είναι κενό.', + 'Confirm Password' => 'Επιβεβαίωση Κωδικού', + 'Confirm password is empty.' => 'Το πεδίο Επιβεβαίωση κωδικού είναι κενό.', + 'Confirm Payment Details' => 'Επιβεβαιώστε τα στοιχεία πληρωμής', + 'Connect' => 'Σύνδεση', + 'Connecting to a hardware wallet.' => 'Σύνδεση σε πορτοφόλι υλικού.', + 'Connecting to a node failed.' => 'Η σύνδεση σε έναν κόμβο απέτυχε.', + 'Connecting to that hardware wallet failed.' => 'Η σύνδεση σε αυτό το πορτοφόλι υλικού απέτυχε.', + /*TODO*///'Connecting to the host failed.' => '', + 'Connecting to the listener failed.' => 'Η σύνδεση με τον ακροατή απέτυχε.', + 'Connecting to the node failed.' => 'Η σύνδεση με τον κόμβο απέτυχε.', + 'Connecting to the prices server failed.' => 'Η σύνδεση με τον διακομιστή τιμών απέτυχε.', + 'Connecting to the recipient failed.' => 'Η σύνδεση με τον παραλήπτη απέτυχε.', + 'Connect the hardware wallet for %1$y to continue getting the payment proof address.' => 'Συνδέστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Connect the hardware wallet for %1$y to continue mining.' => 'Συνδέστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε την εξόρυξη.', + 'Connect the hardware wallet for %1$y to continue receiving a payment.' => 'Συνδέστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε μια πληρωμή.', + 'Connect the hardware wallet for %1$y to continue sending the payment.' => 'Συνδέστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε την αποστολή της πληρωμής.', + 'Connect the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Συνδέστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Connect the hardware wallet for Wallet %1$s to continue mining.' => 'Συνδέστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε την εξόρυξη.', + 'Connect the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Συνδέστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε μια πληρωμή.', + 'Connect the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Συνδέστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε την αποστολή της πληρωμής.', + 'Continue' => 'Συνέχεια', + 'Copy' => 'Αντιγραφή', + 'Copy Address Error' => 'Σφάλμα αντιγραφής διεύθυνσης', + 'Copy ID Error' => 'Σφάλμα αντιγραφής αναγνωριστικού', + 'Copying the address to your clipboard failed.' => 'Η αντιγραφή της διεύθυνσης στο πρόχειρό σας απέτυχε.', + 'Copying the ID to your clipboard failed.' => 'Η αντιγραφή του αναγνωριστικού στο πρόχειρό σας απέτυχε.', + 'Copying the value to your clipboard failed.' => 'Η αντιγραφή της τιμής στο πρόχειρό σας απέτυχε.', + 'Copyright' => 'Πνευματική ιδιοκτησία', + 'Copy Value Error' => 'Σφάλμα τιμής αντιγραφής', + 'Create' => 'Δημιουργία', + 'Created hardware wallet Wallet %1$s.' => 'Δημιουργήθηκε το πορτοφόλι υλικού Πορτοφόλι %1$s.', + 'Created wallet Wallet %1$s.' => 'Δημιουργήθηκε το πορτοφόλι Πορτοφόλι %1$s.', + 'Create Error' => 'Δημιουργία σφάλματος', + 'Create Wallet Error' => 'Δημιουργία σφάλματος πορτοφολιού', + 'Creating slate failed.' => 'Η δημιουργία καρέ απέτυχε.', + 'Creating transaction failed.' => 'Η δημιουργία συναλλαγής απέτυχε.', + 'Creating wallet failed.' => 'Η δημιουργία πορτοφολιού απέτυχε.', + 'Currency of the displayed prices' => 'Νόμισμα των εμφανιζόμενων τιμών', + 'Current password' => 'Τρέχων κωδικός πρόσβασης', + 'Current password is empty.' => 'Ο τρέχων κωδικός πρόσβασης είναι κενός.', + 'Current password is incorrect.' => 'Ο τρέχων κωδικός είναι λανθασμένος.', + 'Custom listener address' => 'Προσαρμοσμένη διεύθυνση ακροατή', + 'Custom node address' => 'Προσαρμοσμένη διεύθυνση κόμβου', + 'Custom node foreign API secret' => 'Προσαρμοσμένο API Secret ξένου κόμβου', + 'Custom Tor proxy address' => 'Προσαρμοσμένη διεύθυνση διακομιστή μεσολάβησης Tor', + 'Default Base Fee' => 'Προεπιλεγμένο Βασικό Τέλος Πληρωμής ', + 'Delete' => 'Διαγράφή', + 'Delete All Wallets' => 'Διαγραφή όλων των πορτοφολιών', + 'Delete All Wallets Error' => 'Σφάλμα διαγραφής όλων των πορτοφολιών', + 'Deleted all wallets.' => 'Διαγράφηκαν όλα τα πορτοφόλια.', + 'Deleted wallet %1$y.' => 'Διαγράφηκε το πορτοφόλι %1$y.', + 'Deleted wallet Wallet %1$s.' => 'Διαγραμμένο πορτοφόλι Πορτοφόλι %1$s.', + 'Delete Error' => 'Σφάλμα Διαγραφής', + /*TODO*///'Description' => '', + 'Destination:' => 'Προορισμός:', + 'Disclaimer' => 'Αποποίηση ευθυνών', + 'Disconnected from the listener.' => 'Αποσυνδέθηκε από τον ακροατή.', + 'Disconnected from the node.' => 'Αποσυνδέθηκε από τον κόμβο.', + 'Displayed address type' => 'Εμφανιζόμενος τύπος διεύθυνσης', + 'Displayed amount type' => 'Εμφανιζόμενος τύπος ποσού', + 'Display prices' => 'Εμφάνιση τιμών', + 'Displays a message when not able to connect to a listener or when disconnected from a listener' => 'Εμφανίζει ένα μήνυμα όταν δεν είναι δυνατή η σύνδεση με έναν ακροατή ή όταν είναι αποσυνδεδεμένος από έναν ακροατή', + 'Displays a message when not able to connect to a node, when a connected node is incompatible, or when disconnected from a node' => 'Εμφανίζει ένα μήνυμα όταν δεν είναι δυνατή η σύνδεση σε έναν κόμβο, όταν ένας συνδεδεμένος κόμβος δεν είναι συμβατός ή όταν είναι αποσυνδεδεμένος από έναν κόμβο', + 'Displays prices for amounts' => 'Εμφανίζει τιμές για ποσά', + /*TODO*///'Donate' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this app.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this extension.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this site.' => '', + 'Don\'t disclose this passphrase to anyone.' => 'Μην αποκαλύπτετε αυτήν τη φράση πρόσβασης σε κανέναν.', + 'Down' => 'Κάτω', + /*TODO*///'Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*///'Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*/'Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Κάθε πορτοφόλι μπορεί να ανακτηθεί μόνο χρησιμοποιώντας τη φράση πρόσβασής του αφού διαγραφεί.', + 'Enable mining API' => 'Ενεργοποίηση API εξόρυξης', + 'Enter a new name for %1$y.' => 'Εισαγάγετε ένα νέο όνομα για το %1$y.', + 'Enter a new name for Wallet %1$s.' => 'Εισαγάγετε ένα νέο όνομα για το Πορτοφόλι %1$s.', + 'Enter a wallet\'s passphrase to recover it.' => 'Εισαγάγετε τη φράση πρόσβασης ενός πορτοφολιού για να το ανακτήσετε.', + 'Enter your password to continue sending the payment.' => 'Εισαγάγετε τον κωδικό πρόσβασής σας για να συνεχίσετε την αποστολή της πληρωμής.', + 'Enter your password to get the passphrase for %1$y.' => 'Εισαγάγετε τον κωδικό πρόσβασής σας για να λάβετε τη φράση πρόσβασης για %1$y.', + 'Enter your password to get the passphrase for Wallet %1$s.' => 'Εισαγάγετε τον κωδικό πρόσβασής σας για να λάβετε τη φράση πρόσβασης για το Πορτοφόλι %1$s.', + /*TODO*///'Enter your pin as the following alphabetic characters to unlock the hardware wallet.' => '', + /*TODO*///'Epic Cash' => '', + /*TODO*///'Epic Cash:' => '', + 'Error' => 'Λάθος', + 'Error %1$s' => 'Σφάλμα %1$s', + 'Error response from the recipient.' => 'Απόκριση σφάλματος από τον παραλήπτη.', + 'Expired' => 'Έχει λήξει', + 'Expired transactions can\'t be canceled.' => 'Οι συναλλαγές που έχουν λήξει δεν μπορούν να ακυρωθούν.', + 'Expired transactions can\'t be rebroadcast.' => 'Οι συναλλαγές που έχουν λήξει δεν μπορούν να αναμεταδοθούν.', + 'Expire height:' => 'Λήξη Height', + 'Exporting the root public key on that %1$x hardware wallet was denied.' => 'Η εξαγωγή του ριζικού δημόσιου κλειδιού σε αυτό το πορτοφόλι υλικού %1$x απορρίφθηκε.', + 'Failed to determine the primary instance.' => 'Αποτυχία προσδιορισμού της κύριας παρουσίας.', + 'Failed to initialize dependencies.' => 'Απέτυχε η προετοιμασία των εξαρτήσεων.', + 'Failed to install or update the service worker.' => 'Αποτυχία εγκατάστασης ή ενημέρωσης του εργάτη σέρβις.', + 'Failed to load resources. Refresh this site to try again.' => 'Αποτυχία φόρτωσης πόρων. Ανανεώστε αυτόν τον ιστότοπο για να προσπαθήσετε ξανά.', + 'Failed to load resources. Restart this app to try again.' => 'Αποτυχία φόρτωσης πόρων. Επανεκκινήστε αυτήν την εφαρμογή για να προσπαθήσετε ξανά.', + 'Fee:' => 'Τέλος Πληρωμής :', + /*TODO*///'Fee\'s value when recorded:' => '', + 'Finalize' => 'Οριστικοποίηση', + 'Finalize the transaction for %1$y to continue sending the payment.' => 'Ολοκληρώστε τη συναλλαγή για %1$y για να συνεχίσετε την αποστολή της πληρωμής.', + 'Finalize the transaction for Wallet %1$s to continue sending the payment.' => 'Ολοκληρώστε τη συναλλαγή για το Πορτοφόλι %1$s για να συνεχίσετε την αποστολή της πληρωμής.', + 'Finalizing the slate failed.' => 'Η οριστικοποίηση της πλάκας απέτυχε.', + 'First' => 'Πρώτα', + 'Floonet' => 'Floonet', + 'Floonet/testnet' => 'Floonet/δίκτυο δοκιμής', + 'Forbidden' => 'Απαγορευμένος', + 'Forbidden response from the recipient.' => 'Απαγορευμένη απάντηση από τον παραλήπτη.', + 'Forward' => 'Εμπρός', + 'From wallet' => 'Από το πορτοφόλι', + 'Gateway Timeout' => 'Πύλη Ώρα αναχώρησης', + /*TODO*///'Get File Response' => '', + /*TODO*///'Get File Response Error' => '', + 'Get Passphrase' => 'Λήψη φράσης πρόσβασης', + 'Get Passphrase Error' => 'Λάβετε Σφάλμα φράσης πρόσβασης', + 'Get Payment Proof Address' => 'Λάβετε διεύθυνση απόδειξης πληρωμής', + 'Get Payment Proof Address Error' => 'Λήψη σφάλματος διεύθυνσης απόδειξης πληρωμής', + 'Getting the app information from that %1$x hardware wallet failed.' => 'Η λήψη των πληροφοριών της εφαρμογής από αυτό το πορτοφόλι υλικού %1$x απέτυχε.', + /*TODO*///'Getting the features from that %1$x hardware wallet failed.' => '', + 'Getting the payment proof address failed.' => 'Η λήψη της διεύθυνσης απόδειξης πληρωμής απέτυχε.', + 'Getting the root public key from that %1$x hardware wallet failed.' => 'Η λήψη του ριζικού δημόσιου κλειδιού από αυτό το πορτοφόλι υλικού %1$x απέτυχε.', + 'Getting the seed cookie from that %1$x hardware wallet failed.' => 'Η λήψη του αρχικού cookie από αυτό το πορτοφόλι υλικού %1$x απέτυχε.', + /*TODO*///'Give the %1$m file to the payment\'s recipient and open their response file to continue sending the payment.' => '', + /*TODO*///'Give the %1$m file to the payment\'s sender for them to finalize the transaction.' => '', + 'Grin' => 'Γκριμάτσα', + 'Grin:' => 'Γκριμάτσα:', + 'Hardware' => 'Υλικό', + 'Hardware wallet' => 'Πορτοφόλι υλικού', + 'Hardware Wallet' => 'Πορτοφόλι Υλικού', + 'Hardware Wallet Approval Requested' => 'Ζητήθηκε έγκριση πορτοφολιού υλικού', + 'Hardware wallet connected' => 'Συνδέθηκε το πορτοφόλι υλικού', + 'Hardware Wallet Disconnected' => 'Το πορτοφόλι υλικού αποσυνδέθηκε', + 'Hardware Wallet Error' => 'Σφάλμα πορτοφολιού υλικού', + 'Hardware Wallet Locked' => 'Το πορτοφόλι υλικού είναι κλειδωμένο', + 'Hardware wallet support' => 'Υποστήριξη πορτοφολιού υλικού', + 'height locked' => 'κλειδωμένο ύψος', + 'Hide' => 'Κρύβω', + 'HTTP' => 'HTTP', + 'HTTPS' => 'HTTPS', + 'ID:' => 'ΤΑΥΤΟΤΗΤΑ:', + 'ID Copied' => 'Η ταυτότητα αντιγράφηκε', + /*TODO*///'If someone asked you to copy/paste something here you are being scammed!!!' => '', + /*TODO*///'If you paid to access MWC Wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back' => '', + 'Inactive lock activated.' => 'Το ανενεργό κλείδωμα ενεργοποιήθηκε.', + 'Incorrect password.' => 'Λάθος κωδικός.', + /*TODO*///'Incorrect pin.' => '', + /*TODO*///'Information' => '', + 'Install Now' => 'Εγκατάσταση τώρα', + 'Install the MWC Wallet app?' => 'Εγκατάσταση της εφαρμογής MWC Wallet;', + 'Insufficient balance.' => 'Ανεπαρκές Υπόλοιπο.', + 'Interface Settings' => 'Ρυθμίσεις διεπαφής', + 'Internal error' => 'Εσωτερικό σφάλμα', + 'Internal Server Error' => 'Εσωτερικό Σφάλμα Διακομιστή', + 'Invalid amount.' => 'Μη έγκυρο ποσό.', + 'Invalid message.' => 'Μη έγκυρο μήνυμα.', + /*TODO*///'Invalid message from the host.' => '', + 'Invalid name.' => 'Μη έγκυρο όνομα.', + 'Invalid origin.' => 'Μη έγκυρη προέλευση.', + 'Invalid parameters' => 'Μη έγκυρες παράμετροι', + 'Invalid passphrase.' => 'Μη έγκυρη φράση πρόσβασης.', + /*TODO*///'Invalid pin.' => '', + 'Invalid recipient address.' => 'Μη έγκυρη διεύθυνση παραλήπτη.', + 'Invalid request' => 'Ακυρη Αίτηση', + 'Invalid request.' => 'Ακυρη Αίτηση.', + 'Invalid request response from the recipient.' => 'Μη έγκυρη απάντηση αιτήματος από τον παραλήπτη.', + 'Invalid response from the recipient.' => 'Μη έγκυρη απάντηση από τον παραλήπτη.', + 'Invalid wallet.' => 'Μη έγκυρο πορτοφόλι.', + 'JavaScript Error' => 'Σφάλμα JavaScript', + 'Kernel excess:' => 'Υπέρβαση πυρήνα:', + 'Language' => 'Γλώσσα', + 'Language changed to %1$y.' => 'Η γλώσσα άλλαξε σε %1$y.', + 'Language\'s currency' => 'Το νόμισμα της γλώσσας', + 'Last' => 'Τελευταίο', + 'Ledger Flex' => 'Ledger Flex', + 'Ledger Nano S' => 'Ledger Nano S', + 'Ledger Nano S Plus' => 'Ledger Nano S Plus', + 'Ledger Nano X' => 'Ledger Nano X', + 'Ledger Stax' => 'Ledger Stax', + 'Listener' => 'Ακροατής', + 'Listener connected' => 'Ο ακροατής συνδέθηκε', + 'Listener disconnected' => 'Ο ακροατής αποσυνδέθηκε', + 'Listener Error' => 'Σφάλμα ακροατή', + 'Listener Settings' => 'Ρυθμίσεις ακροατή', + 'Listener settings changed. Disconnecting from the listener.' => 'Οι ρυθμίσεις του ακροατή άλλαξαν. Αποσύνδεση από τον ακροατή.', + 'Loading…' => 'Φόρτωση…', + 'Loading Error' => 'Σφάλμα φόρτωσης', + 'Lock' => 'Κλειδαριά', + 'Locked.' => 'Κλειδωμένο.', + 'Lock height:' => 'Ύψος κλειδαριάς:', + 'Lock if not focused' => 'Κλείδωμα εάν δεν είναι εστιασμένο', + 'Log' => 'Αρχείο Καταγραφής', + 'Log Error' => 'Σφάλμα καταγραφής', + 'Log initialized.' => 'Αρχικοποιήθηκε το αρχείο καταγραφής.', + /*TODO*///'Login With Wallet' => '', + 'Mainnet' => 'Mainnet', + 'Maintenance' => 'Συντήρηση', + 'Make sure that the correct app is open on the hardware wallet.' => 'Βεβαιωθείτε ότι η σωστή εφαρμογή είναι ανοιχτή στο πορτοφόλι υλικού.', + 'Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.' => 'Βεβαιωθείτε ότι η σωστή εφαρμογή είναι ανοιχτή στο πορτοφόλι υλικού και ότι το πορτοφόλι υλικού δεν είναι κλειδωμένο.', + /*TODO*///'Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m' => '', + /*TODO*///'Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m' => '', + /*TODO*///'Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m' => '', + /*TODO*///'Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m' => '', + 'Manage your MimbleWimble Coin' => 'Διαχειριστείτε το MimbleWimble νόμισμα σας', + 'Match connection' => 'Ταίριασμα σύνδεσης', + 'Maximum number of messages that the log can display' => 'Μέγιστος αριθμός μηνυμάτων που μπορεί να εμφανίσει το αρχείο καταγραφής', + 'Message:' => 'Μήνυμα:', + 'Method not found' => 'Η μέθοδος δεν βρέθηκε', + 'MimbleWimble Coin' => 'Νόμισμα MimbleWimble', + 'MimbleWimble Coin:' => 'Νόμισμα MimbleWimble:', + 'Minutes between price updates' => 'Λεπτά μεταξύ των ενημερώσεων τιμών', + 'Minutes of inactivity required for automatic lock' => 'Λεπτά αδράνειας που απαιτούνται για αυτόματο κλείδωμα', + 'MWC Wallet' => 'Πορτοφόλι MWC', + 'MWC Wallet — Error' => 'Πορτοφόλι MWC — Σφάλμα', + 'MWC Wallet — Error %1$s' => 'Πορτοφόλι MWC — Σφάλμα %1$s', + 'MWC Wallet is an extension that allows you to manage your MimbleWimble Coin in your web browser.' => 'Το MWC Wallet είναι μια επέκταση που σας επιτρέπει να διαχειρίζεστε το MimbleWimble Νόμισμα σας στο πρόγραμμα περιήγησής σας.', + /*TODO*///'MWC Wallet is an open-source, self-custodial wallet that allows you to easily send and receive MimbleWimble Coin. Designed with ease of use and accessibility in mind, this wallet runs on everything from smartwatches to mainframes while providing an intuitive interface that makes using it a breeze.' => '', + 'MWC Wallet is a self-custodial web wallet that allows you to manage your MimbleWimble Coin in your web browser.' => 'Το MWC Wallet είναι ένα δικτυακό πορτοφόλι που σας επιτρέπει να διαχειρίζεστε το MimbleWimble Coin στο πρόγραμμα περιήγησής σας.', + 'MWC Wallet — Maintenance' => 'Πορτοφόλι MWC — Συντήρηση', + 'N/A' => 'N/A', + 'Name' => 'Ονομα', + 'Network changed. Disconnecting from the listener.' => 'Το δίκτυο άλλαξε. Αποσύνδεση από τον ακροατή.', + 'Network type' => 'Τύπος δικτύου', + 'Network type:' => 'Τύπος δικτύου:', + 'New password' => 'Νέος Κωδικός', + 'New password is empty.' => 'Ο νέος κωδικός πρόσβασης είναι κενός.', + 'New passwords don\'t match.' => 'Οι νέοι κωδικοί πρόσβασης δεν ταιριάζουν.', + 'New Wallet Passphrase' => 'Νέα φράση πρόσβασης Πορτοφολιού', + 'Next' => 'Επόμενο', + 'Nicolas Flamel' => 'Nicolas Flamel', + 'No' => 'Οχι', + 'No camera was found. Connect a camera to use this feature.' => 'Δεν βρέθηκε κάμερα. Συνδέστε μια κάμερα για να χρησιμοποιήσετε αυτήν τη δυνατότητα.', + 'Node' => 'Κόμβος', + 'Node connected' => 'Συνδέθηκε ο κόμβος', + 'Node disconnected' => 'Ο κόμβος αποσυνδέθηκε', + 'Node Error' => 'Σφάλμα κόμβου', + 'Node Settings' => 'Ρυθμίσεις κόμβου', + 'Node settings changed. Disconnecting from the node.' => 'Οι ρυθμίσεις κόμβου άλλαξαν. Αποσύνδεση από τον κόμβο.', + 'Node warning' => 'Προειδοποίηση κόμβου', + 'No hardware wallet was selected.' => 'Δεν επιλέχθηκε πορτοφόλι υλικού.', + 'Non-hardware wallet' => 'Πορτοφόλι χωρίς υλικό', + 'no recent duplicate' => 'κανένα πρόσφατο αντίγραφο', + 'Not compatible with node versions less than %1$v.' => 'Δεν είναι συμβατό με εκδόσεις κόμβου μικρότερες από %1$v.', + 'Not Found' => 'Δεν βρέθηκε', + 'Not found response from the recipient.' => 'Δεν βρέθηκε απάντηση από τον παραλήπτη.', + 'No transactions exist for this wallet.' => 'Δεν υπάρχουν συναλλαγές για αυτό το πορτοφόλι.', + 'Number of confirmations:' => 'Αριθμός επιβεβαιώσεων:', + 'Number of confirmations required to spend an output' => 'Αριθμός επιβεβαιώσεων που απαιτούνται για μια έξοδο', + 'OK' => 'Εντάξει', + 'Onion Service' => 'Onion Service', + 'Only one instance of this app can be open at once. Close all other instances to continue.' => 'Μόνο μια σύνδεση αυτής της εφαρμογής μπορεί να ανοίξει ταυτόχρονα. Κλείστε όλες τις άλλες συνδέσεις για να συνεχίσετε.', + 'Only one instance of this extension can be open at once. Close all other instances to continue.' => 'Μόνο ένα intance αυτής της επέκτασης μπορεί να ανοίξει ταυτόχρονα. Κλείστε όλες τις υπόλοιπες για να συνεχίσετε.', + 'Only one instance of this site can be open at once. Close all other instances to continue.' => 'Μόνο μία παρουσία αυτού του ιστότοπου μπορεί να ανοίξει ταυτόχρονα. Κλείστε όλες τις άλλες περιπτώσεις για να συνεχίσετε.', + /*TODO*///'Opening that file failed.' => '', + 'Open in New Tab' => 'Άνοιγμα σε Νέα καρτέλα', + 'Open in New Window' => 'Άνοιγμα σε νέο παράθυρο', + /*TODO*///'Open Response File' => '', + 'Optional message' => 'Προαιρετικό μήνυμα', + 'Order Error' => 'Σφάλμα παραγγελίας', + 'Output commitment:' => 'Δέσμευση παραγωγής:', + 'Passphrase' => 'Φράση πρόσβασης', + 'Passphrases can\'t be retrieved from hardware wallets.' => 'Οι φράσεις πρόσβασης δεν μπορούν να ανακτηθούν από πορτοφόλια υλικού.', + 'Password' => 'Κωδικός πρόσβασης', + 'Password Changed' => 'Ο κωδικός άλλαξε', + 'Password is empty.' => 'Ο κωδικός πρόσβασης είναι κενός.', + 'Passwords don\'t match.' => 'Οι κωδικοί πρόσβασης δεν ταιριάζουν.', + 'Payload too large response from the recipient.' => 'Πολύ μεγάλη απόκριση ωφέλιμου φορτίου από τον παραλήπτη.', + 'Payment Details' => 'Οι λεπτομέρειες πληρωμής', + 'Payment Proof' => 'Απόδειξη πληρωμής', + 'Payment Proof Address' => 'Διεύθυνση απόδειξης πληρωμής', + 'Payment Received' => 'Η πληρωμή ελήφθη', + /*TODO*///'Pin' => '', + 'plain' => 'Απλή Μορφή', + 'Previous' => 'Προηγούμενο', + 'Private Browsing And Site Data Information' => 'Πληροφορίες ιδιωτικής περιήγησης και δεδομένων τοποθεσίας', + /*TODO*///'Rebroadcast' => '', + /*TODO*///'Rebroadcast Error' => '', + 'Rebroadcasting the transaction failed.' => 'Η αναμετάδοση της συναλλαγής απέτυχε.', + 'Rebroadcasting the transaction failed for the following reason.' => 'Η αναμετάδοση της συναλλαγής απέτυχε για τον ακόλουθο λόγο.', + 'Received' => 'Ελήφθη', + 'Received an invalid response from the node.' => 'Λάβατε μια μη έγκυρη απάντηση από τον κόμβο.', + 'Received an invalid response from the prices server.' => 'Λάβατε μια μη έγκυρη απάντηση από τον διακομιστή τιμών.', + 'Received transactions can\'t be rebroadcast.' => 'Οι ληφθείσες συναλλαγές δεν μπορούν να αναμεταδοθούν.', + /*TODO*///'Receive Payment As File' => '', + /*TODO*///'Receive Payment As File Error' => '', + 'Recipient address' => 'Διεύθυνση παραλήπτη', + 'Recipient address is empty.' => 'Η διεύθυνση παραλήπτη είναι κενή.', + 'Recipient address is invalid.' => 'Η διεύθυνση παραλήπτη δεν είναι έγκυρη.', + 'Recipient address isn\'t supported.' => 'Η διεύθυνση παραλήπτη δεν υποστηρίζεται.', + 'Recipient doesn\'t support any available slate versions.' => 'Ο παραλήπτης δεν υποστηρίζει καμία διαθέσιμη έκδοση καρέ.', + 'Recipient doesn\'t support payment proofs.' => 'Ο παραλήπτης δεν υποστηρίζει αποδείξεις πληρωμής.', + 'Recipient payment proof address:' => 'Διεύθυνση απόδειξης πληρωμής παραλήπτη:', + 'Recipient payment proof signature:' => 'Υπογραφή αποδεικτικού πληρωμής παραλήπτη:', + 'Recipient\'s foreign API version isn\'t supported.' => 'Η ξένη έκδοση API του παραλήπτη δεν υποστηρίζεται.', + 'Recipient\'s slate versions aren\'t supported.' => 'Οι εκδόσεις καρέ του παραλήπτη δεν υποστηρίζονται.', + 'Recorded time:' => 'Χρόνος εγγραφής:', + 'Recover' => 'Ανάκτηση', + 'Recovered wallet Wallet %1$s.' => 'Ανακτημένο πορτοφόλι Πορτοφόλι %1$s.', + 'Recover Wallet' => 'Ανάκτηση Πορτοφολιού', + 'Recover Wallet Error' => 'Σφάλμα ανάκτησης πορτοφολιού', + 'Refresh this site to try again.' => 'Ανανεώστε αυτόν τον ιστότοπο για να προσπαθήσετε ξανά.', + 'Relative height is invalid.' => 'Το σχετικό ύψος δεν είναι έγκυρο.', + 'Release date:' => 'Ημερομηνία κυκλοφορίας:', + /*TODO*///'Reload Required' => '', + 'Remind me later' => 'Θύμισέ μου αργότερα', + 'Rename' => 'Μετονομασία', + 'Renamed wallet %1$y to %2$y.' => 'Μετονομάστηκε το πορτοφόλι %1$y σε %2$y.', + 'Renamed wallet Wallet %1$s to %2$y.' => 'Μετονομασία πορτοφολιού Πορτοφόλι %1$s σε %2$y.', + 'Rename Error' => 'Σφάλμα μετονομασίας', + 'Required number of confirmations:' => 'Απαιτούμενος αριθμός επιβεβαιώσεων:', + 'Require payment proof' => 'Απαιτείται απόδειξη πληρωμής', + 'Requires all new transactions to have a payment proof' => 'Απαιτεί όλες οι νέες συναλλαγές να έχουν απόδειξη πληρωμής', + 'Reset settings.' => 'Επαναφορά ρυθμίσεων.', + 'Reset Settings' => 'Επαναφορά ρυθμίσεων', + 'Reset Settings Error' => 'Σφάλμα επαναφοράς ρυθμίσεων', + 'Restart this app to try again.' => 'Επανεκκινήστε αυτήν την εφαρμογή για να προσπαθήσετε ξανά.', + 'Restart this extension to try again.' => 'Επανεκκινήστε αυτήν την επέκταση για να προσπαθήσετε ξανά.', + 'Resync' => 'Εκ νέου συγχρονισμός', + 'Resync Error' => 'Σφάλμα επανασυγχρονισμού', + 'Resyncing can also use a lot of data depending on the number of unspent transaction outputs present in the blockchain.' => 'Ο επανασυγχρονισμός μπορεί επίσης να χρησιμοποιήσει αρκετά δεδομένα ανάλογα με τον αριθμό των μη δαπανημένων εξόδων συναλλαγών που υπάρχουν στο blockchain.', + 'Resyncing can take a significant amount of time to complete, and you won\'t be able to send payments from this wallet until it\'s finished resyncing.' => 'Ο εκ νέου συγχρονισμός μπορεί να χρειαστεί αρκετό χρόνο για να ολοκληρωθεί και δεν θα μπορείτε να στέλνετε πληρωμές από αυτό το πορτοφόλι μέχρι να ολοκληρωθεί ο επανασυγχρονισμός του.', + 'Scan QR Code' => 'Σάρωση κωδικού QR', + 'Scan QR Code Error' => 'Σφάλμα σάρωσης κωδικού QR', + 'Selected' => 'Επιλεγμένο', + 'Select passphrase\'s origin.' => 'Επιλέξτε την προέλευση της φράσης πρόσβασης.', + 'Send' => 'Αποστολή', + /*TODO*///'Send as file' => '', + 'Sender payment proof address:' => 'Διεύθυνση απόδειξης πληρωμής αποστολέα:', + 'Send Error' => 'Αποστολή σφάλματος', + /*TODO*///'Sending %1$c from %2$y for a fee of %3$c.' => '', + 'Sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Αποστολή %1$c από %2$y στην ακόλουθη διεύθυνση με χρέωση %3$c.', + /*TODO*///'Sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'Sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Αποστολή %1$c από το Πορτοφόλι %2$s στην ακόλουθη διεύθυνση με χρέωση %3$c.', + 'Sending Payment' => 'Αποστολή πληρωμής', + 'Send Payment' => 'Αποστολή πληρωμής', + 'Send Payment Error' => 'Σφάλμα αποστολής πληρωμής', + 'Sent' => 'Απεσταλμένα', + /*TODO*///'Sent transactions don\'t have a file response.' => '', + /*TODO*///'Set Payment Proof Address Index' => '', + 'Sets the address for the custom listener' => 'Ορίζει τη διεύθυνση για τον προσαρμοσμένο ακροατή', + 'Sets the address for the custom node' => 'Ορίζει τη διεύθυνση για τον προσαρμοσμένο κόμβο', + 'Sets the address for the custom Tor proxy' => 'Ορίζει τη διεύθυνση για τον προσαρμοσμένο διακομιστή μεσολάβησης Tor', + 'Sets the address type to display for each wallet' => 'Ορίζει τον τύπο διεύθυνσης που θα εμφανίζεται για κάθε πορτοφόλι', + 'Sets the amount type to display in the wallets list' => 'Ορίζει τον τύπο ποσού που θα εμφανίζεται στη λίστα πορτοφολιών', + 'Sets the currency of the displayed prices' => 'Ορίζει το νόμισμα των τιμών που εμφανίζονται', + 'Sets the duration of inactivity required for the automatic lock to occur' => 'Ρυθμίζει τη διάρκεια της αδράνειας που απαιτείται για να συμβεί το αυτόματο κλείδωμα', + 'Sets the duration of time between price updates' => 'Ορίζει τη χρονική διάρκεια μεταξύ των ενημερώσεων τιμών', + 'Sets the foreign API secret for the custom node. Leave this empty if the custom node doesn\'t require a foreign API secret' => 'Ορίζει το API Secret για τον προσαρμοσμένο κόμβο. Αφήστε το κενό εάν ο προσαρμοσμένος κόμβος δεν απαιτεί ξένο API Secret', + 'Sets the maximum number of messages that the log can display. Earlier messages will be removed once this limit is reached' => 'Ορίζει τον μέγιστο αριθμό μηνυμάτων που μπορεί να εμφανίσει το αρχείο καταγραφής. Τα προηγούμενα μηνύματα θα αφαιρεθούν μόλις συμπληρωθεί αυτό το όριο', + /*TODO*///'Sets the network type to use' => '', + 'Sets the number of confirmations required for a new output to be spendable' => 'Ορίζει τον αριθμό των επιβεβαιώσεων που απαιτούνται για να είναι δυνατή η κατανάλωση μιας νέας παραγωγής', + /*TODO*///'Sets the wallet type to use' => '', + 'Settings' => 'Ρυθμίσεις', + 'Settings Error' => 'Σφάλμα ρυθμίσεων', + 'Show' => 'Προβολή', + 'Show listener connection error messages' => 'Εμφάνιση μηνυμάτων σφάλματος σύνδεσης ακροατή', + 'Show node connection error messages' => 'Εμφάνιση μηνυμάτων σφάλματος σύνδεσης κόμβου', + 'Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.' => 'Ορισμένα προγράμματα περιήγησης δεν επιτρέπουν τη σύνδεση με περιεχόμενο που προβάλλεται με μη ασφαλή τρόπο από μια εφαρμογή που προβάλλεται με ασφάλεια.', + 'Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.' => 'Ορισμένα προγράμματα περιήγησης δεν επιτρέπουν τη σύνδεση με περιεχόμενο που προβάλλεται με μη ασφαλή τρόπο από ιστότοπο που εξυπηρετείται με ασφάλεια.', + /*TODO*///'Source code:' => '', + 'Spendable' => 'Δαπανήσιμος', + 'Spendable amount:' => 'Ποσό που δαπανάται:', + 'Spendable height:' => 'Ύψος ποσού δαπάνης:', + 'Status:' => 'Κατάσταση:', + 'Successfully connected to the listener.' => 'Συνδέθηκε επιτυχώς με τον ακροατή.', + 'Successfully connected to the node.' => 'Επιτυχής σύνδεση στον κόμβο.', + 'Successfully connected to the prices server.' => 'Επιτυχής σύνδεση με τον διακομιστή τιμών.', + 'Successfully updated the prices.' => 'Οι τιμές ενημερώθηκαν με επιτυχία.', + 'Synced' => 'Συγχρονίστηκε', + 'Syncing' => 'Συγχρονισμός', + 'Syncing…' => 'Συγχρονισμός…', + 'Syncing failed' => 'Ο συγχρονισμός απέτυχε', + 'Testnet' => 'Δίκτυο δοκιμής', + /*TODO*///'That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.' => '', + /*TODO*///'That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.' => '', + /*TODO*///'That file is invalid, contains unsupported features, or was already used.' => '', + /*TODO*///'That hardware wallet is currently in use.' => '', + 'That hardware wallet isn\'t for %1$y.' => 'Αυτό το πορτοφόλι υλικού δεν είναι για %1$y.', + 'That hardware wallet isn\'t for Wallet %1$s.' => 'Αυτό το πορτοφόλι υλικού δεν είναι για το Πορτοφόλι %1$s.', + /*TODO*///'That hardware wallet was disconnected.' => '', + /*TODO*///'That QR code is invalid.' => '', + 'The address for %1$y was successfully copied to your clipboard.' => 'Η διεύθυνση για %1$y αντιγράφηκε με επιτυχία στο πρόχειρό σας.', + 'The address for Wallet %1$s was successfully copied to your clipboard.' => 'Η διεύθυνση για το Πορτοφόλι %1$s αντιγράφηκε με επιτυχία στο πρόχειρό σας.', + /*TODO*///'The address was successfully copied to your clipboard.' => '', + 'The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.' => 'Η εφαρμογή σε αυτό το πορτοφόλι υλικού %1$x δεν είναι συμβατή. Ενημερώστε την εφαρμογή στο πορτοφόλι υλικού στην έκδοση %2$v ή νεότερη για να συνεχίσετε.', + 'The current height is unknown.' => 'Το τρέχον ύψος είναι άγνωστο.', + 'The database failed.' => 'Η βάση δεδομένων απέτυχε.', + 'The fee changed.' => 'Το τέλος πληρωμής άλλαξε.', + 'The fee is invalid.' => 'Το τέλος πληρωμής δεν είναι έγκυρο.', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.' => '', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.' => '', + 'The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.' => 'Τα ακόλουθα άτομα δημιούργησαν τις μεταφράσεις για αυτήν την εφαρμογή. Μπορείτε να στείλετε email στο %1$m εάν ενδιαφέρεστε να μεταφράσετε αυτήν την εφαρμογή σε άλλη γλώσσα.', + 'The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.' => 'Τα ακόλουθα άτομα δημιούργησαν τις μεταφράσεις για αυτήν την επέκταση. Μπορείτε να στείλετε μήνυμα ηλεκτρονικού ταχυδρομείου στο %1$m εάν σας ενδιαφέρει να μεταφράσετε αυτήν την επέκταση σε άλλη γλώσσα.', + 'The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.' => 'Τα ακόλουθα άτομα δημιούργησαν τις μεταφράσεις για αυτόν τον ιστότοπο. Μπορείτε να στείλετε email στο %1$m εάν ενδιαφέρεστε να μεταφράσετε αυτόν τον ιστότοπο σε άλλη γλώσσα.', + 'The hardware wallet is already connected.' => 'Το πορτοφόλι υλικού είναι ήδη συνδεδεμένο.', + 'The ID for transaction %1$s was successfully copied to your clipboard.' => 'Το αναγνωριστικό για τη συναλλαγή %1$s αντιγράφηκε με επιτυχία στο πρόχειρό σας.', + 'The node isn\'t compatible. The node\'s version must be version %1$v or newer.' => 'Ο κόμβος δεν είναι συμβατός. Η έκδοση του κόμβου πρέπει να είναι η έκδοση %1$v ή νεότερη.', + 'The node may require a foreign API secret.' => 'Ο κόμβος μπορεί να απαιτεί ένα ξένο API Secret.', + 'The node returned an invalid response.' => 'Ο κόμβος επέστρεψε μια μη έγκυρη απάντηση.', + 'The node returned an unauthorized response.' => 'Ο κόμβος επέστρεψε μια μη εξουσιοδοτημένη απάντηση.', + 'The node\'s current height is %1$s.' => 'Το τρέχον ύψος του κόμβου είναι %1$s.', + 'The node\'s new height is %1$s.' => 'Το νέο ύψος του κόμβου είναι %1$s.', + 'The node\'s version is %1$v.' => 'Η έκδοση του κόμβου είναι %1$v.', + 'The passphrase for %1$y is the following passphrase.' => 'Η φράση πρόσβασης για %1$y είναι η ακόλουθη φράση πρόσβασης.', + 'The passphrase for Wallet %1$s is the following passphrase.' => 'Η φράση πρόσβασης για το Πορτοφόλι %1$s είναι η ακόλουθη φράση πρόσβασης.', + 'The payment proof address for %1$y is the following payment proof address.' => 'Η διεύθυνση απόδειξης πληρωμής για %1$y είναι η ακόλουθη διεύθυνση απόδειξης πληρωμής.', + 'The payment proof address for Wallet %1$s is the following payment proof address.' => 'Η διεύθυνση απόδειξης πληρωμής για το Πορτοφόλι %1$s είναι η ακόλουθη διεύθυνση απόδειξης πληρωμής.', + /*TODO*///'The recipient payment proof address you used for the transaction is the following payment proof address.' => '', + 'The recipient responded with the following invalid response.' => 'Ο παραλήπτης απάντησε με την ακόλουθη μη έγκυρη απάντηση.', + /*TODO*///'The sender payment proof address you\'re using for the transaction is the following payment proof address.' => '', + 'The setting doesn\'t exist.' => 'Η ρύθμιση δεν υπάρχει.', + 'The transaction can\'t be rebroadcast.' => 'Η συναλλαγή δεν μπορεί να αναμεταδοθεί.', + 'The transaction doesn\'t belong to the wallet.' => 'Η συναλλαγή δεν ανήκει στο πορτοφόλι.', + 'The transaction doesn\'t exist.' => 'Η συναλλαγή δεν υπάρχει.', + /*TODO*///'The transaction doesn\'t have a file response or its file response isn\'t known.' => '', + /*TODO*///'The transaction doesn\'t have a payment proof.' => '', + 'The transaction is already canceled.' => 'Η συναλλαγή έχει ήδη ακυρωθεί.', + /*TODO*///'The transaction\'s sender payment proof address is the following payment proof address.' => '', + 'The transaction was successfully canceled.' => 'Η συναλλαγή ακυρώθηκε με επιτυχία.', + 'The transaction was successfully rebroadcast.' => 'Η συναλλαγή αναμεταδόθηκε με επιτυχία.', + /*TODO*///'The transaction won\'t have a payment proof.' => '', + 'The update was successfully installed. This app will now reload to use the new version.' => 'Η ενημέρωση εγκαταστάθηκε με επιτυχία. Αυτή η εφαρμογή θα φορτώσει ξανά για να χρησιμοποιήσει τη νέα έκδοση.', + 'The update was successfully installed. This site will now reload to use the new version.' => 'Η ενημέρωση εγκαταστάθηκε με επιτυχία. Αυτός ο ιστότοπος θα επαναφορτωθεί τώρα για να χρησιμοποιήσει τη νέα έκδοση.', + 'The value was successfully copied to your clipboard.' => 'Η τιμή αντιγράφηκε με επιτυχία στο πρόχειρό σας.', + 'The wallet doesn\'t exist.' => 'Το πορτοφόλι δεν υπάρχει.', + 'The wallet is closed.' => 'Το πορτοφόλι είναι κλειστό.', + 'The wallet isn\'t synced.' => 'Το πορτοφόλι δεν είναι συγχρονισμένο.', + 'The wallet\'s address doesn\'t exist.' => 'Η διεύθυνση του πορτοφολιού δεν υπάρχει.', + 'The wallets are locked.' => 'Τα πορτοφόλια είναι κλειδωμένα.', + 'The wallet was successfully renamed.' => 'Το πορτοφόλι μετονομάστηκε με επιτυχία.', + 'Third-Party Cookies Information' => 'Πληροφορίες για τα cookies τρίτων', + 'This app is currently down for maintenance.' => 'Αυτή η εφαρμογή είναι προς το παρόν εκτός λειτουργίας για συντήρηση.', + 'This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.' => 'Αυτή η εφαρμογή παρέχεται "ως έχει", χωρίς καμία εγγύηση, ρητή ή σιωπηρή, συμπεριλαμβανομένων, ενδεικτικά, των εγγυήσεων εμπορευσιμότητας, καταλληλότητας για συγκεκριμένο σκοπό και μη παραβίασης. Σε καμία περίπτωση οι δημιουργοί ή οι κάτοχοι πνευματικών δικαιωμάτων δεν ευθύνονται για οποιαδήποτε αξίωση, ζημιά ή άλλη ευθύνη, είτε πρόκειται για ενέργεια σύμβασης, αδικοπραξία ή άλλη, που προκύπτει από, από ή σε σχέση με αυτήν την εφαρμογή ή τη χρήση ή άλλες συναλλαγές σε αυτήν εφαρμογή.', + /*TODO*///'This app must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This app must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This app utilizes code and assets from the following sources.' => 'Αυτή η εφαρμογή χρησιμοποιεί κώδικα και στοιχεία από τις ακόλουθες πηγές.', + 'This app will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Αυτή η εφαρμογή θα απενεργοποιηθεί λόγω προγραμματισμένης συντήρησης από %1$d στις %2$t.', + 'This app won\'t run when it\'s embedded in a site.' => 'Αυτή η εφαρμογή δεν θα εκτελείται όταν είναι ενσωματωμένη σε έναν ιστότοπο.', + 'This extension injects an API into every website you visit making it possible for those websites to interact with MWC Wallet.' => 'Αυτή η επέκταση εισάγει ένα API σε κάθε ιστότοπο που επισκέπτεστε, δίνοντας τη δυνατότητα σε αυτούς τους ιστότοπους να αλληλεπιδρούν με το MWC Wallet.', + 'This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.' => 'Αυτή η επέκταση παρέχεται "ως έχει", χωρίς καμία εγγύηση, ρητή ή σιωπηρή, συμπεριλαμβανομένων, ενδεικτικά, των εγγυήσεων εμπορευσιμότητας, καταλληλότητας για συγκεκριμένο σκοπό και μη παραβίασης. Σε καμία περίπτωση οι δημιουργοί ή οι κάτοχοι πνευματικών δικαιωμάτων δεν ευθύνονται για οποιαδήποτε αξίωση, ζημιά ή άλλη ευθύνη, είτε πρόκειται για ενέργεια σύμβασης, αδικοπραξία ή άλλη, που προκύπτει από, από ή σε σχέση με αυτήν την επέκταση ή τη χρήση ή άλλες συναλλαγές σε αυτό επέκταση.', + /*TODO*///'This extension must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This extension must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This extension utilizes code and assets from the following sources.' => 'Αυτή η επέκταση χρησιμοποιεί κώδικα και στοιχεία από τις ακόλουθες πηγές.', + 'This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Αυτή η επέκταση δεν θα λειτουργήσει σωστά εάν το πρόγραμμα περιήγησής σας έχει ρυθμιστεί να αποκλείει τα cookies τρίτων. Βεβαιωθείτε ότι το πρόγραμμα περιήγησής σας έχει ρυθμιστεί ώστε να επιτρέπει cookies τρίτων προτού συνεχίσετε.', + 'This extension won\'t run when it\'s embedded in a site.' => 'Αυτή η επέκταση δεν θα εκτελείται όταν είναι ενσωματωμένη σε έναν ιστότοπο.', + 'This may take several minutes to complete. The recipient must be online and listening at that address to receive this payment.' => 'Μπορεί να χρειαστούν αρκετά λεπτά για να ολοκληρωθεί. Ο παραλήπτης πρέπει να είναι συνδεδεμένος και να ακούει σε αυτήν τη διεύθυνση για να λάβει αυτήν την πληρωμή.', + 'This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.' => 'Αυτή η φράση πρόσβασης θα σας επιτρέψει να ανακτήσετε το Πορτοφόλι %1$s. Συνιστάται η εγγραφή αυτής της φράσης πρόσβασης με ασφαλή, μη ψηφιακό τρόπο.', + 'This site is currently down for maintenance.' => 'Αυτή η τοποθεσία είναι προς το παρόν εκτός λειτουργίας για συντήρηση.', + 'This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.' => 'Αυτός ο ιστότοπος παρέχεται "ως έχει", χωρίς καμία εγγύηση, ρητή ή σιωπηρή, συμπεριλαμβανομένων, ενδεικτικά, των εγγυήσεων εμπορευσιμότητας, καταλληλότητας για συγκεκριμένο σκοπό και μη παραβίασης. Σε καμία περίπτωση οι δημιουργοί ή οι κάτοχοι πνευματικών δικαιωμάτων δεν ευθύνονται για οποιαδήποτε αξίωση, ζημιά ή άλλη ευθύνη, είτε πρόκειται για ενέργεια σύμβασης, αδικοπραξία ή άλλη, που προκύπτει από, από ή σε σχέση με αυτόν τον ιστότοπο ή τη χρήση ή άλλες συναλλαγές σε αυτόν ιστοσελίδα.', + /*TODO*///'This site must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This site must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This site uses cookies that are essential to its functionality. By using this site\'s services or clicking OK, you agree to this site\'s use of cookies.' => 'Αυτός ο ιστότοπος χρησιμοποιεί cookies που είναι απαραίτητα για τη λειτουργικότητά του. Χρησιμοποιώντας τις υπηρεσίες αυτού του ιστότοπου ή κάνοντας κλικ στο OK, συμφωνείτε με τη χρήση των cookies από αυτόν τον ιστότοπο.', + 'This site utilizes code and assets from the following sources.' => 'Αυτός ο ιστότοπος χρησιμοποιεί κώδικα και στοιχεία από τις ακόλουθες πηγές.', + 'This site will be down for scheduled maintenance starting on %1$d at %2$t.' => 'Αυτός ο ιστότοπος θα απενεργοποιηθεί λόγω προγραμματισμένης συντήρησης από %1$d στις %2$t.', + 'This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.' => 'Αυτός ο ιστότοπος δεν θα λειτουργεί σωστά εάν έχετε ενεργοποιήσει τις λειτουργίες ιδιωτικής ή ανώνυμης περιήγησης ή εάν το πρόγραμμα περιήγησής σας έχει ρυθμιστεί ώστε να διαγράφει αυτόματα cookie και δεδομένα ιστότοπου. Βεβαιωθείτε ότι οι λειτουργίες ιδιωτικής περιήγησης και ανώνυμης περιήγησης είναι απενεργοποιημένες και ότι το πρόγραμμα περιήγησής σας έχει ρυθμιστεί ώστε να διατηρεί cookie και δεδομένα ιστότοπου προτού συνεχίσετε.', + 'This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => 'Αυτός ο ιστότοπος δεν θα λειτουργεί σωστά εάν το πρόγραμμα περιήγησής σας έχει ρυθμιστεί να αποκλείει τα cookies τρίτων. Βεβαιωθείτε ότι το πρόγραμμα περιήγησής σας έχει ρυθμιστεί ώστε να επιτρέπει cookies τρίτων προτού συνεχίσετε.', + 'This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.' => 'Αυτός ο ιστότοπος δεν θα εκτελείται όταν είναι ενσωματωμένος σε έναν ιστότοπο. Επισκεφτείτε το %1$l για να συνεχίσετε.', + 'This wallet can only be recovered by using its passphrase once it\'s been deleted.' => 'Αυτό το πορτοφόλι μπορεί να ανακτηθεί μόνο χρησιμοποιώντας τη φράση πρόσβασής του αφού διαγραφεί.', + /*TODO*/'This wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => 'Αυτό το πορτοφόλι μπορεί να ανακτηθεί μόνο χρησιμοποιώντας τη φράση πρόσβασής του αφού διαγραφεί.', + /*TODO*///'This wallet is available free of charge and should not be sold in any format. If you paid to access this wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back.' => '', + 'Time to live cut off height must be greater than or equal to the lock height.' => 'Το ύψος αποκοπής χρόνου ζωής πρέπει να είναι μεγαλύτερο ή ίσο με το ύψος της κλειδαριάς.', + 'Time to live cut off height must be greater than the current height.' => 'Το ύψος αποκοπής του χρόνου ζωής πρέπει να είναι μεγαλύτερο από το τρέχον ύψος.', + 'Tor Proxy Settings' => 'Ρυθμίσεις διακομιστή μεσολάβησης Tor', + 'Transaction %1$s' => 'Συναλλαγή %1$s', + 'Transaction Canceled' => 'Η συναλλαγή ακυρώθηκε', + 'Transaction Error' => 'Σφάλμα συναλλαγής', + 'Transaction Rebroadcast' => 'Αναμετάδοση συναλλαγής', + 'Transactions' => 'Συναλλαγές', + 'Transactions Navigation Error' => 'Σφάλμα πλοήγησης συναλλαγών', + 'Translation Contributors' => 'Συντελεστές Μετάφρασης', + /*TODO*///'Trezor' => '', + /*TODO*///'Trezor Model One' => '', + /*TODO*///'Trezor Model T' => '', + /*TODO*///'Trezor Safe 3' => '', + /*TODO*///'Trezor Safe 5' => '', + 'Trying to connect to the listener at %1$y.' => 'Προσπάθεια σύνδεσης με τον ακροατή στο %1$y.', + 'Trying to connect to the node at %1$y.' => 'Προσπάθεια σύνδεσης στον κόμβο στο %1$y.', + 'Trying to connect to the prices server at %1$y.' => 'Προσπάθεια σύνδεσης στον διακομιστή τιμών στο %1$y.', + 'Type:' => 'Τύπος:', + 'Unauthorized' => 'Μη Εξουσιοδότημενος', + 'Unauthorized response from the recipient.' => 'Μη εξουσιοδοτημένη απάντηση από τον παραλήπτη.', + 'Unbroadcast transactions can\'t be rebroadcast.' => 'Οι μη μεταδιδόμενες συναλλαγές δεν μπορούν να αναμεταδοθούν.', + 'Unconfirmed' => 'Μη επιβεβαιωμένο ', + 'Unconfirmed amount:' => 'Μη επιβεβαιωμένο ποσό:', + 'Unknown' => 'Αγνωστο', + 'Unlock' => 'Ξεκλείδωμα', + 'Unlocked.' => 'Ακλείδωτο.', + 'Unlock Error' => 'Σφάλμα ξεκλειδώματος', + /*TODO*///'Unlock that hardware wallet to continue connecting to it.' => '', + 'Unlock the hardware wallet for %1$y to continue getting the payment proof address.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Unlock the hardware wallet for %1$y to continue mining.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε την εξόρυξη.', + 'Unlock the hardware wallet for %1$y to continue receiving a payment.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε μια πληρωμή.', + 'Unlock the hardware wallet for %1$y to continue sending the payment.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για %1$y για να συνεχίσετε την αποστολή της πληρωμής.', + 'Unlock the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Unlock the hardware wallet for Wallet %1$s to continue mining.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε την εξόρυξη.', + 'Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε μια πληρωμή.', + 'Unlock the hardware wallet for Wallet %1$s to continue sending the payment.' => 'Ξεκλειδώστε το πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε την αποστολή της πληρωμής.', + 'Unsupported response from the recipient.' => 'Μη υποστηριζόμενη απάντηση από τον παραλήπτη.', + 'Up' => 'Πάνω', + 'Update Available' => 'Διαθέσιμη ενημέρωση', + 'Update Installed' => 'Εγκαταστάθηκε η ενημέρωση', + 'Update your browser to use this feature.' => 'Ενημερώστε το πρόγραμμα περιήγησής σας για να χρησιμοποιήσετε αυτήν τη δυνατότητα.', + 'Updating the prices failed.' => 'Η ενημέρωση των τιμών απέτυχε.', + 'URL' => 'Σύνδεσμος', + 'Use custom listener' => 'Χρησιμοποιήστε προσαρμοσμένο ακροατή', + 'Use custom node' => 'Χρησιμοποιήστε προσαρμοσμένο κόμβο', + 'Use custom Tor proxy' => 'Χρησιμοποιήστε προσαρμοσμένο διακομιστή μεσολάβησης Tor', + 'Uses a custom listener instead of the default listener' => 'Χρησιμοποιεί ένα προσαρμοσμένο πρόγραμμα ακρόασης αντί του προεπιλεγμένου ακροατή', + 'Uses a custom node instead of the default nodes' => 'Χρησιμοποιεί έναν προσαρμοσμένο κόμβο αντί για τους προεπιλεγμένους κόμβους', + 'Uses a custom Tor proxy instead of the default Tor proxy' => 'Χρησιμοποιεί έναν προσαρμοσμένο διακομιστή μεσολάβησης Tor αντί του προεπιλεγμένου διακομιστή μεσολάβησης Tor', + 'Utilities' => 'Βοηθητικά προγράμματα', + 'Value:' => 'Αξία:', + 'Value: %1$c' => 'Τιμή: %1$c', + 'Value Copied' => 'Τιμή αντιγράφηκε', + 'Value in %1$y' => 'Τιμή σε %1$y', + 'Verifying the payment proof address failed.' => 'Η επαλήθευση της διεύθυνσης απόδειξης πληρωμής απέτυχε.', + 'Verifying the Slatepack address on the hardware wallet failed.' => 'Η επαλήθευση της διεύθυνσης Slatepack στο πορτοφόλι υλικού απέτυχε.', + 'Verifying the Tor address on the hardware wallet failed.' => 'Η επαλήθευση της διεύθυνσης Tor στο πορτοφόλι υλικού απέτυχε.', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no recipient payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the recipient payment proof address displayed matches the following payment proof address.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.' => '', + 'Verify that the node\'s foreign API secret is correct.' => 'Βεβαιωθείτε ότι το ξένο API Secret του κόμβου είναι σωστό.', + 'Verify that the pasted address matches the following address when you paste it.' => 'Βεβαιωθείτε ότι η επικολλημένη διεύθυνση ταιριάζει με την ακόλουθη διεύθυνση όταν την επικολλάτε.', + 'Verify that the pasted ID matches the following ID when you paste it.' => 'Βεβαιωθείτε ότι το επικολλημένο αναγνωριστικό ταιριάζει με το ακόλουθο αναγνωριστικό όταν το επικολλάτε.', + 'Verify that the pasted value matches the following value when you paste it.' => 'Βεβαιωθείτε ότι η επικολλημένη τιμή ταιριάζει με την ακόλουθη τιμή όταν την επικολλάτε.', + 'Verify that the Slatepack address displayed on the hardware wallet matches the following payment proof address.' => 'Βεβαιωθείτε ότι η διεύθυνση Slatepack που εμφανίζεται στο πορτοφόλι υλικού ταιριάζει με την ακόλουθη διεύθυνση απόδειξης πληρωμής.', + 'Verify that the Tor address displayed on the hardware wallet matches the following payment proof address.' => 'Βεβαιωθείτε ότι η διεύθυνση Tor που εμφανίζεται στο πορτοφόλι υλικού ταιριάζει με την ακόλουθη διεύθυνση απόδειξης πληρωμής.', + 'Verify the payment proof address on the hardware wallet for %1$y to continue getting the payment proof address.' => 'Επαληθεύστε τη διεύθυνση απόδειξης πληρωμής στο πορτοφόλι υλικού για %1$y για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Verify the payment proof address on the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => 'Επαληθεύστε τη διεύθυνση απόδειξης πληρωμής στο πορτοφόλι υλικού για το Πορτοφόλι %1$s για να συνεχίσετε να λαμβάνετε τη διεύθυνση απόδειξης πληρωμής.', + 'Version %1$v' => 'Έκδοση %1$v', + 'Version Changes' => 'Αλλαγές έκδοσης', + 'Version Information' => 'Πληροφορίες έκδοσης', + 'Version number:' => 'Αριθμός έκδοσης:', + 'Wallet %1$s' => 'Πορτοφόλι %1$s', + 'Wallet Error' => 'Σφάλμα Πορτοφολιού', + 'Wallet Renamed' => 'Το πορτοφόλι μετονομάστηκε', + 'Wallets' => 'Πορτοφόλια', + 'Wallet Settings' => 'Ρυθμίσεις πορτοφολιού', + 'Wallet type' => 'Τύπος πορτοφολιού', + 'Wallet type:' => 'Τύπος πορτοφολιού:', + 'Website API integration' => 'Ενσωμάτωση API ιστότοπου', + 'Yes' => 'Ναί', + 'You aren\'t connected to a listener.' => 'Δεν είστε συνδεδεμένοι με έναν ακροατή.', + /*TODO*///'You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.' => '', + 'You can guarantee that this payment is going to the intended recipient by having the recipient confirm that this payment proof address is their payment proof address.' => 'Μπορείτε να εγγυηθείτε ότι αυτή η πληρωμή θα γίνει στον παραλήπτη που θέλετε, ζητώντας από τον παραλήπτη να επιβεβαιώσει ότι αυτή η διεύθυνση απόδειξης πληρωμής είναι η διεύθυνση απόδειξης πληρωμής του.', + 'You can only receive payments at this address while you\'re online and connected to a listener.' => 'Μπορείτε να λαμβάνετε πληρωμές σε αυτήν τη διεύθυνση μόνο όταν είστε συνδεδεμένοι και συνδεδεμένοι με έναν ακροατή.', + /*TODO*///'You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.' => '', + 'You can\'t guarantee that this payment is going to the intended recipient since the transaction doesn\'t have a payment proof.' => 'Δεν μπορείτε να εγγυηθείτε ότι αυτή η πληρωμή θα γίνει στον προοριζόμενο παραλήπτη, καθώς αυτή η συναλλαγή δεν διαθέτει απόδειξη πληρωμής.', + /*TODO*///'You\'ll be sending %1$c from %2$y for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from %2$y to the following address for a fee of %3$c.' => 'Θα στείλετε %1$c από %2$y στην ακόλουθη διεύθυνση έναντι χρέωσης %3$c.', + /*TODO*///'You\'ll be sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => 'Θα στείλετε %1$c από το Πορτοφόλι %2$s στην ακόλουθη διεύθυνση έναντι τέλους πληρωμής %3$c.', + 'You\'ll need to provide a Tor proxy address to connect to the node.' => 'Θα χρειαστεί να δώσετε μια διεύθυνση διακομιστή μεσολάβησης Tor για να συνδεθείτε στον κόμβο.', + 'You\'ll need to provide a Tor proxy address to connect to the recipient.' => 'Θα χρειαστεί να δώσετε μια διεύθυνση διακομιστή μεσολάβησης Tor για να συνδεθείτε με τον παραλήπτη.', + 'You\'ll no longer be able to receive payments at the wallet\'s current address once its address suffix has been changed.' => 'Δεν θα μπορείτε πλέον να λαμβάνετε πληρωμές στην τρέχουσα διεύθυνση του πορτοφολιού, αφού αλλάξει το επίθημα διεύθυνσής του.', + 'You may need to be already paired with the device before this app can connect to it.' => 'Ίσως χρειαστεί να έχετε ήδη ζευγαρώσει με τη συσκευή για να συνδεθεί αυτή η εφαρμογή σε αυτήν.', + 'You may need to be already paired with the device before this extension can connect to it.' => 'Ίσως χρειαστεί να έχετε ήδη ζευγαρώσει με τη συσκευή για να συνδεθεί αυτή η επέκταση σε αυτήν.', + 'You may need to be already paired with the device before this site can connect to it.' => 'Ίσως χρειαστεί να έχετε ήδη ζευγαρώσει με τη συσκευή για να συνδεθεί αυτός ο ιστότοπος σε αυτήν.', + /*TODO*///'You may need to disconnect and reconnect the hardware wallet to connect to it.' => '', + 'You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.' => 'Ίσως χρειαστεί να ανοίξετε αυτήν την επέκταση σε μια καρτέλα ή ένα παράθυρο, εάν δεν μπορεί να συνδεθεί σε ένα πορτοφόλι υλικού.', + 'You may need to specify a different Tor proxy address to connect to the node.' => 'Ίσως χρειαστεί να καθορίσετε μια διαφορετική διεύθυνση διακομιστή μεσολάβησης Tor για να συνδεθείτε στον κόμβο.', + 'You may need to specify a different Tor proxy address to connect to the recipient.' => 'Ίσως χρειαστεί να καθορίσετε μια διαφορετική διεύθυνση διακομιστή μεσολάβησης Tor για να συνδεθείτε με τον παραλήπτη.', + 'You may need to specify a listener address that is served over HTTPS to connect to the listener.' => 'Ίσως χρειαστεί να καθορίσετε μια διεύθυνση ακροατή που θα εξυπηρετείται μέσω HTTPS για να συνδεθείτε με τον ακροατή.', + 'You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.' => 'Ίσως χρειαστεί να καθορίσετε μια διεύθυνση κόμβου που θα εξυπηρετείται μέσω HTTPS ή ως υπηρεσία Onion για να συνδεθείτε στον κόμβο.', + 'You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.' => 'Ίσως χρειαστεί να καθορίσετε μια διεύθυνση παραλήπτη που θα εξυπηρετείται μέσω HTTPS ή ως υπηρεσία Onion για να συνδεθείτε με τον παραλήπτη.', + 'Your browser doesn\'t allow using a camera.' => 'Το πρόγραμμα περιήγησής σας δεν επιτρέπει τη χρήση κάμερας.', + 'Your browser doesn\'t allow using USB or Bluetooth devices.' => 'Το πρόγραμμα περιήγησής σας δεν επιτρέπει τη χρήση συσκευών USB ή Bluetooth.', + 'Your browser doesn\'t support JavaScript. Update your browser and/or enable JavaScript to continue.' => 'Το πρόγραμμα περιήγησής σας δεν υποστηρίζει JavaScript. Ενημερώστε το πρόγραμμα περιήγησής σας ή/και ενεργοποιήστε το JavaScript για να συνεχίσετε.', + 'Your browser isn\'t compatible. Update your browser to continue.' => 'Το πρόγραμμα περιήγησής σας δεν είναι συμβατό. Ενημερώστε το πρόγραμμα περιήγησής σας για να συνεχίσετε.', + 'You\'re no longer connected to a node.' => 'Δεν είστε πλέον συνδεδεμένοι σε έναν κόμβο.', + 'You\'re no longer connected to the listener.' => 'Δεν είστε πλέον συνδεδεμένοι με τον ακροατή.', + 'You\'re no longer connected to the node.' => 'Δεν είστε πλέον συνδεδεμένοι με τον κόμβο.', + 'You\'re not authorized to connect to the node.' => 'Δεν είστε εξουσιοδοτημένοι να συνδεθείτε στον κόμβο.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction doesn\'t have a payment proof.' => 'Στέλνετε %1$c με τέλος πληρωμής %2$c και αυτή η συναλλαγή δεν διαθέτει απόδειξη πληρωμής.', + 'You\'re sending %1$c for a fee of %2$c, and the transaction\'s recipient payment proof address is the following payment proof address.' => 'Στέλνετε %1$c με τέλος πληρωμής %2$c και η διεύθυνση απόδειξης πληρωμής του παραλήπτη είναι η ακόλουθη διεύθυνση απόδειξης πληρωμής.', + 'Your password was successfully changed.' => 'Ο κωδικός πρόσβασής σας άλλαξε με επιτυχία.', + 'You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.' => 'Δεν θα πρέπει να θεωρείτε νόμιμη αυτή την πληρωμή μέχρι να γίνει.', + 'You were sent %1$c to %2$y.' => 'Έχετε σταλεί %1$c στο %2$y.', + 'You were sent %1$c to %2$y with a message.' => 'Σας εστάλη %1$c στο %2$y με ένα μήνυμα.', + 'You were sent %1$c to Wallet %2$s.' => 'Στείλατε %1$c στο Πορτοφόλι %2$s.', + 'You were sent %1$c to Wallet %2$s with a message.' => 'Λάβατε %1$c στο Πορτοφόλι %2$s με ένα μήνυμα.', + 'You won\'t be able to change address suffixes without being connected to a listener.' => 'Δεν θα μπορείτε να αλλάξετε τα επιθήματα διευθύνσεων χωρίς να συνδεθείτε με έναν ακροατή.', + 'You won\'t be able to rebroadcast transactions without being connected to a node.' => 'Δεν θα μπορείτε να αναμεταδώσετε συναλλαγές χωρίς να είστε συνδεδεμένοι σε έναν κόμβο.', + 'You won\'t be able to receive payments without being connected to a listener.' => 'Δεν θα μπορείτε να λαμβάνετε πληρωμές χωρίς να είστε συνδεδεμένοι με έναν ακροατή.', + 'You won\'t be able to send payments without being connected to a node.' => 'Δεν θα μπορείτε να στέλνετε πληρωμές χωρίς να είστε συνδεδεμένοι σε έναν κόμβο.', + ] + ]; +?> diff --git a/languages/simplified_chinese.php b/languages/simplified_chinese.php new file mode 100755 index 0000000..633a59f --- /dev/null +++ b/languages/simplified_chinese.php @@ -0,0 +1,731 @@ + [ + "grincalf" => "https://twitter.com/@hhffnmkhgf" + ], + + // Constants + "Constants" => [ + + // Language + "Language" => "中文", + + // Direction + "Direction" => "ltr", + + // Image + "Image" => "./images/countries/china.svg", + + // Currency + "Currency" => "CNY", + + // Extension locale code + "Extension Locale Code" => "zh-CN" + ], + + // Text + "Text" => [ + '(?<=,) ' => '', + '(?<=.) ' => '', + '(?<=:) ' => '', + ',(?= )' => ',', + ' (%1$c)' => '(%1$c)', + '%1$d at %2$t' => '%2$t中的%1$d', + '#%1$s' => '#%1$s', + '%1$s%%' => '%1$s%%', + '%1$s–%2$s' => '%1$s–%2$s', + '© %1$s–%2$s Nicolas Flamel.' => '© %1$s–%2$s Nicolas Flamel.', + '© %1$s Nicolas Flamel.' => '© %1$s Nicolas Flamel.', + '%1$u:' => '%1$u:', + '%1$x/%2$x/v%3$v' => '%1$x/%2$x/v%3$v', + '%1$y: %2$m' => '%1$y:%2$m', + '%1$y: %2$m, License: %3$m' => '%1$y:%2$m,许可证:%3$m', + 'About' => '关于', + 'About Error' => '关于错误', + 'Access to your camera was denied.' => '无法访问您的相机。', + 'Account' => '账户', + 'Account Error' => '账户错误', + 'Address' => '地址', + 'Address:' => '地址:', + 'Address Copied' => '地址已复制', + 'A listener address hasn\'t been provided.' => '未提供监听器地址。', + 'All' => '全部', + 'All Error' => '全部错误', + 'Allow changing base fee' => '允许更改交易的基本费用', + 'Allows changing a transaction\'s base fee when sending a payment' => '发送付款时允许更改交易的基本费用', + 'Allows processing mining related API requests' => '允许处理与挖矿相关的API请求', + 'A MimbleWimble Coin wallet.' => 'MimbleWimble币钱包。', + 'Amount' => '金额', + 'Amount:' => '金额:', + 'Amount: %1$c' => '金额:%1$c', + 'Amount is empty.' => '金额为空。', + 'Amount is invalid.' => '金额无效。', + /*TODO*///'Amount\'s value when recorded:' => '', + 'An error has occurred. This app will automatically reload in a few seconds.' => '发生错误。此应用程序将在几秒钟内自动重新加载。', + 'An error has occurred. This site will automatically reload in a few seconds.' => '发生错误。此网站将在几秒钟内自动重新加载。', + 'An error occurred while trying to access your camera.' => '尝试访问您的摄像头时发生错误。', + /*TODO*///'An internal error occurred.' => '', + 'A node address hasn\'t been provided.' => '未提供节点地址。', + 'An operation is currently being performed on the wallet\'s address suffix.' => '当前正在对钱包的地址后缀执行操作。', + 'An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.' => '此应用程序有更新可用。您现在要安装更新吗?如果是,则此应用程序将在更新安装后重新加载。否则,更新将在您下次打开此应用程序时安装。', + 'An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.' => '此网站有更新可用。您现在要安装更新吗?如果是,则此网站将在更新安装后重新加载。否则,更新将在您下次访问此网站时安装。', + 'API Settings' => 'API设置', + 'Approve exporting the root public key for the account at index %1$s on that hardware wallet.' => '批准在该硬件钱包上导出索引为%1$s的帐户的根公钥。', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue mining.' => '为钱包 %1$y 在硬件钱包上批准接收交易以继续挖掘。', + 'Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.' => '为钱包 %1$y 在硬件钱包上批准接收交易以继续接收付款。', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.' => '为钱包 %1$s 在硬件钱包上批准接收交易以继续挖掘。', + 'Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.' => '为钱包 %1$s 在硬件钱包上批准接收交易,以继续接收付款。', + /*TODO*///'Approve Receiving Payment' => '', + 'Approve sending the transaction on the hardware wallet for %1$y to continue sending the payment.' => '为 %1$y 在硬件钱包上批准发送交易,以继续发送付款。', + 'Approve sending the transaction on the hardware wallet for Wallet %1$s to continue sending the payment.' => '为钱包 %1$s 在硬件钱包上批准发送交易,以继续发送付款。', + 'Approving the transaction on the hardware wallet was denied.' => '拒绝在硬件钱包上批准交易。', + 'Are you sure you want to cancel transaction %1$s?' => '您确定要取消交易%1$s吗?', + 'Are you sure you want to change the address suffix for %1$y?' => '您确定要更改%1$y的地址后缀吗?', + 'Are you sure you want to change the address suffix for Wallet %1$s?' => '您确定要更改钱包%1$s的地址后缀吗?', + 'Are you sure you want to delete %1$y?' => '您确定要删除%1$y吗?', + 'Are you sure you want to delete all your wallets?' => '您确定要删除您的所有钱包吗?', + 'Are you sure you want to delete Wallet %1$s?' => '您确定要删除钱包%1$s吗?', + 'Are you sure you want to exit? There\'s a remaining transaction.' => '您确定要退出吗?还有一笔交易未完成。', + 'Are you sure you want to exit? There\'s remaining transactions.' => '您确定要退出吗?还有未完成的交易。', + 'Are you sure you want to reset the settings to their default values?' => '您确定要将设置重置为默认值吗?', + 'Are you sure you want to resync %1$y?' => '您确定要重新同步%1$y吗?', + 'Are you sure you want to resync Wallet %1$s?' => '您确定要重新同步钱包%1$s吗?', + 'Attributions' => '版权声明', + /*TODO*///'Automatically approve receiving payments' => '', + /*TODO*///'Automatically approves all received payments without requiring manual approval' => '', + 'Automatically lock after a set duration of inactivity' => '在一段时间内不活动后自动锁定', + 'Automatically lock when not focused' => '在失去焦点时自动锁定', + 'Automatic lock activated.' => '自动锁定已激活。', + 'A wallet can\'t send payments to itself.' => '一个钱包不能向自己发送付款。', + 'A wallet with the same passphrase already exists in your list of wallets.' => '已存在具有相同密钥的钱包在您的钱包列表中。', + 'A wallet with the same root public key already exists in your list of wallets.' => '具有相同根公钥的钱包已存在于您的钱包列表中。', + 'Back' => '返回', + 'Bad Gateway' => '网关错误', + 'Balance' => '余额', + 'Base fee' => '基础费用', + 'Base fee is empty.' => '基础费用为空。', + 'Base fee is invalid.' => '基础费用无效。', + /*TODO*///'Bitcoin:' => '', + 'Broadcasting the transaction failed.' => '广播交易失败。', + 'Broadcasting the transaction failed for the following reason.' => '广播交易失败,原因如下。', + 'Broadcast transactions can\'t be canceled.' => '广播交易无法被取消。', + 'Cancel' => '取消', + 'Canceled' => '已取消', + 'Canceled transactions can\'t be rebroadcast.' => '已取消的交易无法重新广播。', + /*TODO*///'Cancel Error' => '', + 'Change Address Suffix' => '更改地址后缀', + 'Change Address Suffix Error' => '更改地址后缀错误', + 'Changed %1$y setting.' => '已更改%1$y设置。', + 'Changed %1$y setting to %2$y.' => '将%1$y设置更改为%2$y。', + 'Changed password.' => '密码已更改。', + 'Change Password' => '更改密码', + 'Change Password Error' => '更改密码错误', + 'Changing the address suffix failed.' => '更改地址后缀失败。', + 'Changing the address suffix timed out.' => '更改地址后缀超时。', + 'Changing your password failed.' => '更改密码失败。', + 'coinbase' => 'coinbase', + 'Coinbase' => 'Coinbase', + 'Coinbase transactions can\'t be rebroadcast.' => 'Coinbase交易不能被重新广播。', + /*TODO*///'Coinbase transactions don\'t have a file response.' => '', + 'Compatibility Error' => '兼容性错误', + 'Confirmed' => '已确认', + 'Confirmed amount:' => '已确认的金额:', + 'Confirmed and unconfirmed' => '已确认和未确认', + 'Confirmed height:' => '已确认高度:', + 'Confirmed transactions can\'t be canceled.' => '已确认的交易不能被取消。', + 'Confirmed transactions can\'t be rebroadcast.' => '已确认的交易不能被重新广播。', + 'Confirm new password' => '确认新密码', + 'Confirm new password is empty.' => '两次输入的新密码不一致。', + 'Confirm Password' => '确认密码', + 'Confirm password is empty.' => '两次输入的密码不一致。', + 'Confirm Payment Details' => '确认付款详情', + 'Connect' => '连接', + 'Connecting to a hardware wallet.' => '连接到硬件钱包。', + 'Connecting to a node failed.' => '连接节点失败。', + 'Connecting to that hardware wallet failed.' => '连接到该硬件钱包失败。', + /*TODO*///'Connecting to the host failed.' => '', + 'Connecting to the listener failed.' => '连接监听器失败。', + 'Connecting to the node failed.' => '连接节点失败。', + 'Connecting to the prices server failed.' => '连接价格服务器失败。', + 'Connecting to the recipient failed.' => '连接接收方失败。', + 'Connect the hardware wallet for %1$y to continue getting the payment proof address.' => '连接硬件钱包 %1$y 以继续获取付款证明地址。', + 'Connect the hardware wallet for %1$y to continue mining.' => '连接硬件钱包 %1$y 以继续挖矿。', + 'Connect the hardware wallet for %1$y to continue receiving a payment.' => '连接硬件钱包 %1$y 以继续接收付款。', + 'Connect the hardware wallet for %1$y to continue sending the payment.' => '连接硬件钱包 %1$y 以继续发送付款。', + 'Connect the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => '连接钱包 %1$s 的硬件钱包以继续获取付款证明地址。', + 'Connect the hardware wallet for Wallet %1$s to continue mining.' => '连接钱包 %1$s 的硬件钱包以继续挖矿。', + 'Connect the hardware wallet for Wallet %1$s to continue receiving a payment.' => '连接钱包 %1$s 的硬件钱包以继续接收付款。', + 'Connect the hardware wallet for Wallet %1$s to continue sending the payment.' => '连接钱包 %1$s 的硬件钱包以继续发送付款。', + 'Continue' => '继续', + 'Copy' => '复制', + 'Copy Address Error' => '复制地址错误', + 'Copy ID Error' => '复制ID错误', + 'Copying the address to your clipboard failed.' => '复制地址到剪贴板失败。', + 'Copying the ID to your clipboard failed.' => '复制ID到剪贴板失败。', + 'Copying the value to your clipboard failed.' => '复制数值到剪贴板失败。', + 'Copyright' => '版权', + 'Copy Value Error' => '复制数值错误', + 'Create' => '创建', + 'Created hardware wallet Wallet %1$s.' => '已创建硬件钱包 Wallet %1$s。', + 'Created wallet Wallet %1$s.' => '已创建钱包 Wallet %1$s。', + 'Create Error' => '创建错误', + 'Create Wallet Error' => '创建钱包错误', + 'Creating slate failed.' => '创建交易失败。', + 'Creating transaction failed.' => '创建交易失败。', + 'Creating wallet failed.' => '创建钱包失败。', + 'Currency of the displayed prices' => '显示价格的货币', + 'Current password' => '当前密码', + 'Current password is empty.' => '当前密码为空。', + 'Current password is incorrect.' => '当前密码不正确。', + 'Custom listener address' => '自定义监听器地址', + 'Custom node address' => '自定义节点地址', + 'Custom node foreign API secret' => '自定义节点外部API密钥', + 'Custom Tor proxy address' => '自定义Tor代理地址', + 'Default Base Fee' => '默认基础费用', + 'Delete' => '删除', + 'Delete All Wallets' => '删除所有钱包', + 'Delete All Wallets Error' => '删除所有钱包出错', + 'Deleted all wallets.' => '所有钱包已删除。', + 'Deleted wallet %1$y.' => '已删除钱包%1$y。', + 'Deleted wallet Wallet %1$s.' => '已删除钱包 Wallet %1$s。', + 'Delete Error' => '删除出错', + /*TODO*///'Description' => '', + 'Destination:' => '目标地址:', + 'Disclaimer' => '免责声明', + 'Disconnected from the listener.' => '与监听器断开连接。', + 'Disconnected from the node.' => '与节点断开连接。', + 'Displayed address type' => '显示的地址类型', + 'Displayed amount type' => '显示的金额类型', + 'Display prices' => '显示价格', + 'Displays a message when not able to connect to a listener or when disconnected from a listener' => '无法连接到监听器或与监听器断开连接时显示消息', + 'Displays a message when not able to connect to a node, when a connected node is incompatible, or when disconnected from a node' => '无法连接到节点,连接的节点不兼容或与节点断开连接时显示消息', + 'Displays prices for amounts' => '为金额显示价格', + /*TODO*///'Donate' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this app.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this extension.' => '', + /*TODO*///'Donations are greatly appreciated and help fund the development of this site.' => '', + 'Don\'t disclose this passphrase to anyone.' => '不要将此密钥透露给任何人。', + 'Down' => '向下', + /*TODO*///'Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*///'Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?' => '', + /*TODO*/'Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => '每个钱包仅可通过使用其密钥恢复一次,一旦已删除,就无法再次恢复。', + 'Enable mining API' => '启用挖矿API', + 'Enter a new name for %1$y.' => '为%1$y输入一个新名称。', + 'Enter a new name for Wallet %1$s.' => '为钱包%1$s输入一个新名称。', + 'Enter a wallet\'s passphrase to recover it.' => '输入钱包的密钥以恢复它。', + 'Enter your password to continue sending the payment.' => '输入密码以继续发送付款。', + 'Enter your password to get the passphrase for %1$y.' => '输入密码以获取%1$y的密钥。', + 'Enter your password to get the passphrase for Wallet %1$s.' => '输入密码以获取钱包%1$s的密钥。', + /*TODO*///'Enter your pin as the following alphabetic characters to unlock the hardware wallet.' => '', + /*TODO*///'Epic Cash' => '', + /*TODO*///'Epic Cash:' => '', + 'Error' => '错误', + 'Error %1$s' => '错误%1$s', + 'Error response from the recipient.' => '收件人的错误响应。', + 'Expired' => '已过期', + 'Expired transactions can\'t be canceled.' => '已过期的交易无法取消。', + 'Expired transactions can\'t be rebroadcast.' => '已过期的交易无法重新广播。', + 'Expire height:' => '到期高度:', + 'Exporting the root public key on that %1$x hardware wallet was denied.' => '拒绝在%1$x硬件钱包上导出根公钥。', + 'Failed to determine the primary instance.' => '无法确定主要实例。', + 'Failed to initialize dependencies.' => '无法初始化依赖项。', + 'Failed to install or update the service worker.' => '无法安装或更新服务工作者。', + 'Failed to load resources. Refresh this site to try again.' => '无法加载资源。刷新此站点以重试。', + 'Failed to load resources. Restart this app to try again.' => '无法加载资源。重新启动此应用以重试。', + 'Fee:' => '费用:', + /*TODO*///'Fee\'s value when recorded:' => '', + 'Finalize' => '完成交易', + 'Finalize the transaction for %1$y to continue sending the payment.' => '确认交易以继续发送支付至%1$y。', + 'Finalize the transaction for Wallet %1$s to continue sending the payment.' => '确认交易以继续发送支付至钱包%1$s。', + 'Finalizing the slate failed.' => '交易完结失败。', + 'First' => '第一个', + 'Floonet' => '弗洛网', + 'Floonet/testnet' => '弗洛网/测试网络', + 'Forbidden' => '禁止访问', + 'Forbidden response from the recipient.' => '接收方返回禁止访问。', + 'Forward' => '前进', + 'From wallet' => '从钱包', + 'Gateway Timeout' => '网关超时', + /*TODO*///'Get File Response' => '', + /*TODO*///'Get File Response Error' => '', + 'Get Passphrase' => '获取密码', + 'Get Passphrase Error' => '获取密码错误', + 'Get Payment Proof Address' => '获取支付证明地址', + 'Get Payment Proof Address Error' => '获取支付证明地址错误', + 'Getting the app information from that %1$x hardware wallet failed.' => '无法从硬件钱包%1$x获取应用程序信息。', + /*TODO*///'Getting the features from that %1$x hardware wallet failed.' => '', + 'Getting the payment proof address failed.' => '获取支付证明地址失败。', + 'Getting the root public key from that %1$x hardware wallet failed.' => '无法从硬件钱包%1$x获取根公钥。', + 'Getting the seed cookie from that %1$x hardware wallet failed.' => '从硬件钱包%1$x获取种子 cookie 失败。', + /*TODO*///'Give the %1$m file to the payment\'s recipient and open their response file to continue sending the payment.' => '', + /*TODO*///'Give the %1$m file to the payment\'s sender for them to finalize the transaction.' => '', + 'Grin' => 'Grin', + 'Grin:' => 'Grin:', + 'Hardware' => '硬件', + 'Hardware wallet' => '硬件钱包', + 'Hardware Wallet' => '硬件钱包', + 'Hardware Wallet Approval Requested' => '请求硬件钱包批准', + 'Hardware wallet connected' => '硬件钱包已连接', + 'Hardware Wallet Disconnected' => '硬件钱包已断开连接', + 'Hardware Wallet Error' => '硬件钱包错误', + 'Hardware Wallet Locked' => '硬件钱包已锁定', + 'Hardware wallet support' => '硬件钱包支持', + 'height locked' => '锁定高度', + 'Hide' => '隐藏', + 'HTTP' => 'HTTP', + 'HTTPS' => 'HTTPS', + 'ID:' => 'ID:', + 'ID Copied' => 'ID已复制', + /*TODO*///'If someone asked you to copy/paste something here you are being scammed!!!' => '', + /*TODO*///'If you paid to access MWC Wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back' => '', + 'Inactive lock activated.' => '已激活非活动锁。', + 'Incorrect password.' => '密码不正确。', + /*TODO*///'Incorrect pin.' => '', + /*TODO*///'Information' => '', + 'Install Now' => '立即安装', + 'Install the MWC Wallet app?' => '安装MWC钱包应用程序吗?', + 'Insufficient balance.' => '余额不足。', + 'Interface Settings' => '界面设置', + 'Internal error' => '内部错误', + 'Internal Server Error' => '内部服务器错误', + 'Invalid amount.' => '无效金额。', + 'Invalid message.' => '无效消息。', + /*TODO*///'Invalid message from the host.' => '', + 'Invalid name.' => '无效名称。', + 'Invalid origin.' => '无效来源。', + 'Invalid parameters' => '无效参数', + 'Invalid passphrase.' => '无效密码。', + /*TODO*///'Invalid pin.' => '', + 'Invalid recipient address.' => '无效收件人地址。', + 'Invalid request' => '无效请求', + 'Invalid request.' => '无效请求。', + 'Invalid request response from the recipient.' => '收件人无效请求响应。', + 'Invalid response from the recipient.' => '收件人无效响应。', + 'Invalid wallet.' => '无效钱包。', + 'JavaScript Error' => 'JavaScript错误', + 'Kernel excess:' => '交易核:', + 'Language' => '语言', + 'Language changed to %1$y.' => '语言已更改为%1$y。', + 'Language\'s currency' => '语言的货币', + 'Last' => '最后', + 'Ledger Flex' => 'Ledger Flex', + 'Ledger Nano S' => 'Ledger Nano S', + 'Ledger Nano S Plus' => 'Ledger Nano S Plus', + 'Ledger Nano X' => 'Ledger Nano X', + 'Ledger Stax' => 'Ledger Stax', + 'Listener' => '监听器', + 'Listener connected' => '监听器已连接', + 'Listener disconnected' => '监听器已断开连接', + 'Listener Error' => '监听器错误', + 'Listener Settings' => '监听器设置', + 'Listener settings changed. Disconnecting from the listener.' => '监听器设置已更改。正在断开与监听器的连接。', + 'Loading…' => '正在加载…', + 'Loading Error' => '加载错误', + 'Lock' => '锁定', + 'Locked.' => '已锁定。', + 'Lock height:' => '锁定高度:', + 'Lock if not focused' => '如果未聚焦,则锁定', + 'Log' => '日志', + 'Log Error' => '日志错误', + 'Log initialized.' => '日志已初始化。', + /*TODO*///'Login With Wallet' => '', + 'Mainnet' => '主网络', + 'Maintenance' => '维护', + 'Make sure that the correct app is open on the hardware wallet.' => '确保硬件钱包上打开了正确的应用程序。', + 'Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.' => '确保在硬件钱包上打开正确的应用程序并且硬件钱包未锁定。', + /*TODO*///'Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m' => '', + /*TODO*///'Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m' => '', + /*TODO*///'Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m' => '', + /*TODO*///'Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m' => '', + /*TODO*///'Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m' => '', + 'Manage your MimbleWimble Coin' => '管理您的MimbleWimble币', + 'Match connection' => '匹配连接', + 'Maximum number of messages that the log can display' => '日志可以显示的最大消息数', + 'Message:' => '消息:', + 'Method not found' => '未找到方法', + 'MimbleWimble Coin' => 'MimbleWimble币', + 'MimbleWimble Coin:' => 'MimbleWimble币:', + 'Minutes between price updates' => '价格更新之间的分钟数', + 'Minutes of inactivity required for automatic lock' => '自动锁定所需的不活动时间(分钟)', + 'MWC Wallet' => 'MWC钱包', + 'MWC Wallet — Error' => 'MWC钱包 - 错误', + 'MWC Wallet — Error %1$s' => 'MWC钱包 - 错误%1$s', + 'MWC Wallet is an extension that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC钱包是一款扩展程序,可让您在Web浏览器中管理MimbleWimble币。', + /*TODO*///'MWC Wallet is an open-source, self-custodial wallet that allows you to easily send and receive MimbleWimble Coin. Designed with ease of use and accessibility in mind, this wallet runs on everything from smartwatches to mainframes while providing an intuitive interface that makes using it a breeze.' => '', + 'MWC Wallet is a self-custodial web wallet that allows you to manage your MimbleWimble Coin in your web browser.' => 'MWC钱包是一款自我托管的Web钱包,可让您在Web浏览器中管理MimbleWimble币。', + 'MWC Wallet — Maintenance' => 'MWC钱包 - 维护', + 'N/A' => 'N/A', + 'Name' => '名称', + 'Network changed. Disconnecting from the listener.' => '网络已更改。正在断开与侦听器的连接。', + 'Network type' => '网络类型', + 'Network type:' => '网络类型:', + 'New password' => '新密码', + 'New password is empty.' => '新密码为空。', + 'New passwords don\'t match.' => '新密码不匹配。', + 'New Wallet Passphrase' => '新钱包口令', + 'Next' => '下一个', + 'Nicolas Flamel' => 'Nicolas Flamel', + 'No' => '否', + 'No camera was found. Connect a camera to use this feature.' => '未找到摄像头。连接摄像头以使用此功能。', + 'Node' => '节点', + 'Node connected' => '节点已连接', + 'Node disconnected' => '节点已断开连接', + 'Node Error' => '节点错误', + 'Node Settings' => '节点设置', + 'Node settings changed. Disconnecting from the node.' => '节点设置已更改。正在断开与节点的连接。', + 'Node warning' => '节点警告', + 'No hardware wallet was selected.' => '未选择硬件钱包。', + 'Non-hardware wallet' => '非硬件钱包', + 'no recent duplicate' => '没有最近的副本', + 'Not compatible with node versions less than %1$v.' => '不兼容节点版本小于%1$v。', + 'Not Found' => '未找到', + 'Not found response from the recipient.' => '未找到收件人的响应。', + 'No transactions exist for this wallet.' => '此钱包不存在交易。', + 'Number of confirmations:' => '确认数量:', + 'Number of confirmations required to spend an output' => '要花费输出所需的确认数量', + 'OK' => '确定', + 'Onion Service' => '洋葱服务', + 'Only one instance of this app can be open at once. Close all other instances to continue.' => '此应用只能同时打开一个实例。关闭所有其他实例以继续。', + 'Only one instance of this extension can be open at once. Close all other instances to continue.' => '此扩展程序只能同时打开一个实例。关闭所有其他实例以继续。', + 'Only one instance of this site can be open at once. Close all other instances to continue.' => '此站点只能同时打开一个实例。关闭所有其他实例以继续。', + /*TODO*///'Opening that file failed.' => '', + 'Open in New Tab' => '在新标签页中打开', + 'Open in New Window' => '在新窗口中打开', + /*TODO*///'Open Response File' => '', + 'Optional message' => '可选消息', + 'Order Error' => '订单错误', + 'Output commitment:' => '输出承诺:', + 'Passphrase' => '密钥', + 'Passphrases can\'t be retrieved from hardware wallets.' => '密钥无法从硬件钱包中检索。', + 'Password' => '密码', + 'Password Changed' => '密码已更改', + 'Password is empty.' => '密码为空。', + 'Passwords don\'t match.' => '密码不匹配。', + 'Payload too large response from the recipient.' => '收件人响应的有效负载太大。', + 'Payment Details' => '付款详情', + 'Payment Proof' => '支付证明', + 'Payment Proof Address' => '支付证明地址', + 'Payment Received' => '已收到付款', + /*TODO*///'Pin' => '', + 'plain' => '纯文本', + 'Previous' => '前一个', + 'Private Browsing And Site Data Information' => '私人浏览和网站数据信息', + /*TODO*///'Rebroadcast' => '', + /*TODO*///'Rebroadcast Error' => '', + 'Rebroadcasting the transaction failed.' => '重新广播交易失败。', + 'Rebroadcasting the transaction failed for the following reason.' => '重新广播交易失败的原因如下。', + 'Received' => '已收到', + 'Received an invalid response from the node.' => '从节点接收到无效响应。', + 'Received an invalid response from the prices server.' => '从价格服务器接收到无效响应。', + 'Received transactions can\'t be rebroadcast.' => '无法重新广播接收的交易。', + /*TODO*///'Receive Payment As File' => '', + /*TODO*///'Receive Payment As File Error' => '', + 'Recipient address' => '收件人地址', + 'Recipient address is empty.' => '收件人地址为空。', + 'Recipient address is invalid.' => '收件人地址无效。', + 'Recipient address isn\'t supported.' => '收件人地址不受支持。', + 'Recipient doesn\'t support any available slate versions.' => '收件人不支持任何可用的slate版本。', + 'Recipient doesn\'t support payment proofs.' => '收件人不支持支付证明。', + 'Recipient payment proof address:' => '收款方支付证明地址:', + 'Recipient payment proof signature:' => '收款方支付证明签名:', + 'Recipient\'s foreign API version isn\'t supported.' => '收件人的外部API版本不受支持。', + 'Recipient\'s slate versions aren\'t supported.' => '收件人的slate版本不受支持。', + 'Recorded time:' => '记录时间:', + 'Recover' => '恢复', + 'Recovered wallet Wallet %1$s.' => '已恢复钱包%1$s。', + 'Recover Wallet' => '恢复钱包', + 'Recover Wallet Error' => '恢复钱包错误', + 'Refresh this site to try again.' => '刷新此站点以重试。', + 'Relative height is invalid.' => '相对高度无效。', + 'Release date:' => '发布日期:', + /*TODO*///'Reload Required' => '', + 'Remind me later' => '稍后提醒我', + 'Rename' => '重命名', + 'Renamed wallet %1$y to %2$y.' => '将钱包%1$y重命名为%2$y。', + 'Renamed wallet Wallet %1$s to %2$y.' => '将钱包%1$s重命名为%2$y。', + 'Rename Error' => '重命名错误', + 'Required number of confirmations:' => '所需确认数:', + 'Require payment proof' => '需要支付证明', + 'Requires all new transactions to have a payment proof' => '要求所有新交易都有支付证明', + 'Reset settings.' => '重置设置。', + 'Reset Settings' => '重置设置', + 'Reset Settings Error' => '重置设置错误', + 'Restart this app to try again.' => '重新启动此应用程序以重试。', + 'Restart this extension to try again.' => '重新启动此扩展程序以重试。', + 'Resync' => '重新同步', + 'Resync Error' => '重新同步错误', + 'Resyncing can also use a lot of data depending on the number of unspent transaction outputs present in the blockchain.' => '重新同步也可能会使用大量数据,取决于区块链中存在的未使用交易输出数量。', + 'Resyncing can take a significant amount of time to complete, and you won\'t be able to send payments from this wallet until it\'s finished resyncing.' => '重新同步可能需要相当长的时间才能完成,并且在它完成重新同步之前,您将无法从该钱包发送付款。', + 'Scan QR Code' => '扫描二维码', + 'Scan QR Code Error' => '扫描二维码错误', + 'Selected' => '已选择', + 'Select passphrase\'s origin.' => '选择密钥的来源。', + 'Send' => '发送', + /*TODO*///'Send as file' => '', + 'Sender payment proof address:' => '发送方支付证明地址:', + 'Send Error' => '发送错误', + /*TODO*///'Sending %1$c from %2$y for a fee of %3$c.' => '', + 'Sending %1$c from %2$y to the following address for a fee of %3$c.' => '以%3$c的费用从%2$y发送%1$c到以下地址。', + /*TODO*///'Sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'Sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => '以%3$c的费用从钱包%2$s发送%1$c到以下地址。', + 'Sending Payment' => '发送付款', + 'Send Payment' => '发送付款', + 'Send Payment Error' => '发送付款错误', + 'Sent' => '已发送', + /*TODO*///'Sent transactions don\'t have a file response.' => '', + /*TODO*///'Set Payment Proof Address Index' => '', + 'Sets the address for the custom listener' => '设置自定义侦听器的地址', + 'Sets the address for the custom node' => '设置自定义节点的地址', + 'Sets the address for the custom Tor proxy' => '设置自定义Tor代理的地址', + 'Sets the address type to display for each wallet' => '设置每个钱包要显示的地址类型', + 'Sets the amount type to display in the wallets list' => '设置在钱包列表中要显示的金额类型', + 'Sets the currency of the displayed prices' => '设置显示价格的货币', + 'Sets the duration of inactivity required for the automatic lock to occur' => '设置自动锁定所需的不活动时间', + 'Sets the duration of time between price updates' => '设置价格更新之间的时间间隔', + 'Sets the foreign API secret for the custom node. Leave this empty if the custom node doesn\'t require a foreign API secret' => '为自定义节点设置外部API密钥。如果自定义节点不需要外部API密钥,请将其留空', + 'Sets the maximum number of messages that the log can display. Earlier messages will be removed once this limit is reached' => '设置日志可以显示的最大消息数。一旦达到此限制,较早的消息将被删除', + /*TODO*///'Sets the network type to use' => '', + 'Sets the number of confirmations required for a new output to be spendable' => '设置新输出可支配所需的确认数', + /*TODO*///'Sets the wallet type to use' => '', + 'Settings' => '设置', + 'Settings Error' => '设置错误', + 'Show' => '显示', + 'Show listener connection error messages' => '显示侦听器连接错误消息', + 'Show node connection error messages' => '显示节点连接错误消息', + 'Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.' => '某些浏览器不允许从安全提供的应用程序连接到不安全提供的内容。', + 'Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.' => '某些浏览器不允许从安全提供的网站连接到不安全提供的内容。', + /*TODO*///'Source code:' => '', + 'Spendable' => '可支配的', + 'Spendable amount:' => '可支配金额:', + 'Spendable height:' => '可支配高度:', + 'Status:' => '状态:', + 'Successfully connected to the listener.' => '成功连接到侦听器。', + 'Successfully connected to the node.' => '成功连接到节点。', + 'Successfully connected to the prices server.' => '成功连接到价格服务器。', + 'Successfully updated the prices.' => '价格更新成功。', + 'Synced' => '同步完成', + 'Syncing' => '同步中', + 'Syncing…' => '正在同步…', + 'Syncing failed' => '同步失败', + 'Testnet' => '测试网络', + /*TODO*///'That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.' => '', + /*TODO*///'That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.' => '', + /*TODO*///'That file is invalid, contains unsupported features, or was already used.' => '', + /*TODO*///'That hardware wallet is currently in use.' => '', + 'That hardware wallet isn\'t for %1$y.' => '该硬件钱包不适用于%1$y。', + 'That hardware wallet isn\'t for Wallet %1$s.' => '该硬件钱包不适用于钱包%1$s。', + /*TODO*///'That hardware wallet was disconnected.' => '', + /*TODO*///'That QR code is invalid.' => '', + 'The address for %1$y was successfully copied to your clipboard.' => '地址%1$y已成功复制到剪贴板。', + 'The address for Wallet %1$s was successfully copied to your clipboard.' => '钱包%1$s的地址已成功复制到剪贴板。', + /*TODO*///'The address was successfully copied to your clipboard.' => '', + 'The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.' => '该%1$x硬件钱包上的应用程序不兼容。请更新硬件钱包上的应用程序到版本%2$v或更高版本以继续。', + 'The current height is unknown.' => '当前高度未知。', + 'The database failed.' => '数据库失败。', + 'The fee changed.' => '手续费已更改。', + 'The fee is invalid.' => '手续费无效。', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.' => '', + /*TODO*///'The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.' => '', + 'The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.' => '以下人员为此应用程序创建了翻译。如果您有兴趣将此应用程序翻译成其他语言,请发送电子邮件至%1$m。', + 'The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.' => '以下人员为此扩展创建了翻译。如果您有兴趣将此扩展翻译成其他语言,请发送电子邮件至%1$m。', + 'The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.' => '以下人员为此网站创建了翻译。如果您有兴趣将此网站翻译成其他语言,请发送电子邮件至%1$m。', + 'The hardware wallet is already connected.' => '该硬件钱包已连接。', + 'The ID for transaction %1$s was successfully copied to your clipboard.' => '交易%1$s的ID已成功复制到剪贴板。', + 'The node isn\'t compatible. The node\'s version must be version %1$v or newer.' => '该节点不兼容。该节点的版本必须是%1$v或更高版本。', + 'The node may require a foreign API secret.' => '该节点可能需要外部API密钥。', + 'The node returned an invalid response.' => '该节点返回了无效的响应。', + 'The node returned an unauthorized response.' => '该节点返回了未经授权的响应。', + 'The node\'s current height is %1$s.' => '该节点的当前高度为%1$s。', + 'The node\'s new height is %1$s.' => '该节点的新高度为%1$s。', + 'The node\'s version is %1$v.' => '该节点版本为 %1$v。', + 'The passphrase for %1$y is the following passphrase.' => '%1$y 的口令短语如下。', + 'The passphrase for Wallet %1$s is the following passphrase.' => '钱包 %1$s 的口令短语如下。', + 'The payment proof address for %1$y is the following payment proof address.' => '%1$y 的付款证明地址如下。', + 'The payment proof address for Wallet %1$s is the following payment proof address.' => '钱包 %1$s 的付款证明地址如下。', + /*TODO*///'The recipient payment proof address you used for the transaction is the following payment proof address.' => '', + 'The recipient responded with the following invalid response.' => '接收者的回应是以下无效回应。', + /*TODO*///'The sender payment proof address you\'re using for the transaction is the following payment proof address.' => '', + 'The setting doesn\'t exist.' => '此设置不存在。', + 'The transaction can\'t be rebroadcast.' => '该交易无法重新广播。', + 'The transaction doesn\'t belong to the wallet.' => '该交易不属于此钱包。', + 'The transaction doesn\'t exist.' => '该交易不存在。', + /*TODO*///'The transaction doesn\'t have a file response or its file response isn\'t known.' => '', + /*TODO*///'The transaction doesn\'t have a payment proof.' => '', + 'The transaction is already canceled.' => '该交易已经取消。', + /*TODO*///'The transaction\'s sender payment proof address is the following payment proof address.' => '', + 'The transaction was successfully canceled.' => '该交易已成功取消。', + 'The transaction was successfully rebroadcast.' => '该交易已成功重新广播。', + /*TODO*///'The transaction won\'t have a payment proof.' => '', + 'The update was successfully installed. This app will now reload to use the new version.' => '更新已成功安装。此应用程序将重新加载以使用新版本。', + 'The update was successfully installed. This site will now reload to use the new version.' => '更新已成功安装。此站点将重新加载以使用新版本。', + 'The value was successfully copied to your clipboard.' => '值已成功复制到您的剪贴板。', + 'The wallet doesn\'t exist.' => '此钱包不存在。', + 'The wallet is closed.' => '此钱包已关闭。', + 'The wallet isn\'t synced.' => '此钱包未同步。', + 'The wallet\'s address doesn\'t exist.' => '该钱包的地址不存在。', + 'The wallets are locked.' => '钱包已锁定。', + 'The wallet was successfully renamed.' => '钱包已成功重命名。', + 'Third-Party Cookies Information' => '第三方 Cookie 信息', + 'This app is currently down for maintenance.' => '此应用正在进行维护。', + 'This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.' => '此应用以原样提供,不提供任何明示或暗示的保证,包括但不限于适销性、特定目的适用性和非侵权性保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任承担责任,无论是合同、侵权还是其他原因,产生于此应用或使用此应用或其他交易中', + /*TODO*///'This app must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This app must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This app utilizes code and assets from the following sources.' => '此应用使用了以下来源的代码和资产。', + 'This app will be down for scheduled maintenance starting on %1$d at %2$t.' => '此应用将于%1$d的%2$t开始进行定期维护。', + 'This app won\'t run when it\'s embedded in a site.' => '嵌入网站时此应用无法运行。', + 'This extension injects an API into every website you visit making it possible for those websites to interact with MWC Wallet.' => '此扩展在您访问的每个网站中注入一个 API,使这些网站能够与 MWC 钱包进行交互。', + 'This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.' => '此扩展以原样提供,不提供任何明示或暗示的保证,包括但不限于适销性、特定目的适用性和非侵权性保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任承担责任,无论是合同、侵权还是其他原因,产生于此扩展或使用此扩展或其他交易中', + /*TODO*///'This extension must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This extension must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This extension utilizes code and assets from the following sources.' => '此扩展使用了以下来源的代码和资产。', + 'This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => '如果您的浏览器配置为阻止第三方 Cookie,则此扩展将无法正常运行。在继续之前,请确保您的浏览器已配置为允许第三方 Cookie', + 'This extension won\'t run when it\'s embedded in a site.' => '如果该扩展嵌入到网站中,它将无法运行。', + 'This may take several minutes to complete. The recipient must be online and listening at that address to receive this payment.' => '这可能需要几分钟的时间才能完成。收款方必须在线并在该地址上收听以接收此付款。', + 'This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.' => '此密语将允许您恢复钱包 %1$s。建议您以安全的非数字方式记录此密语。', + 'This site is currently down for maintenance.' => '本网站目前正在进行维护。', + 'This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.' => '本网站按原样提供,不附带任何形式的明示或暗示保证,包括但不限于适销性、特定目的的适用性和非侵权性。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任承担任何责任,无论是合同、侵权行为还是其他方式引起的,与本网站或使用本网站有关的或与本网站交易有关的。', + /*TODO*///'This site must reload for the new network type to be applied. Would you like to reload now?' => '', + /*TODO*///'This site must reload for the new wallet type to be applied. Would you like to reload now?' => '', + 'This site uses cookies that are essential to its functionality. By using this site\'s services or clicking OK, you agree to this site\'s use of cookies.' => '本网站使用对其功能至关重要的 Cookie。通过使用本站的服务或点击“确定”,您同意本站使用 Cookie。', + 'This site utilizes code and assets from the following sources.' => '本网站利用以下来源的代码和资产。', + 'This site will be down for scheduled maintenance starting on %1$d at %2$t.' => '本网站将于%2$t的%1$d开始计划维护。', + 'This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.' => '如果您的浏览器启用了私密浏览模式或隐身模式,或者您的浏览器配置为自动删除 Cookie 和站点数据,则本网站将无法正常运行。在继续之前,请确保禁用了私密浏览和隐身浏览模式,并且您的浏览器已配置为保留 Cookie 和站点数据。', + 'This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.' => '如果您的浏览器配置为阻止第三方 Cookie,则本网站将无法正常运行。在继续之前,请确保您的浏览器已配置为允许第三方 Cookie。', + 'This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.' => '如果该网站嵌入到另一个网站中,它将无法运行。请访问%1$l以继续。', + 'This wallet can only be recovered by using its passphrase once it\'s been deleted.' => '此钱包只能在删除后使用其密语进行恢复。', + /*TODO*/'This wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.' => '此钱包只能在删除后使用其密语进行恢复。', + /*TODO*///'This wallet is available free of charge and should not be sold in any format. If you paid to access this wallet or received this wallet as part of a paid service then you\'ve been scammed and should demand your money back.' => '', + 'Time to live cut off height must be greater than or equal to the lock height.' => '存活截止高度必须大于或等于锁定高度。', + 'Time to live cut off height must be greater than the current height.' => '存活截止高度必须大于当前高度。', + 'Tor Proxy Settings' => 'Tor代理设置', + 'Transaction %1$s' => '交易 %1$s', + 'Transaction Canceled' => '交易已取消', + 'Transaction Error' => '交易错误', + 'Transaction Rebroadcast' => '交易重播', + 'Transactions' => '交易记录', + 'Transactions Navigation Error' => '交易记录导航错误', + 'Translation Contributors' => '翻译贡献者', + /*TODO*///'Trezor' => '', + /*TODO*///'Trezor Model One' => '', + /*TODO*///'Trezor Model T' => '', + /*TODO*///'Trezor Safe 3' => '', + /*TODO*///'Trezor Safe 5' => '', + 'Trying to connect to the listener at %1$y.' => '正在尝试连接到 %1$y 上的监听器。', + 'Trying to connect to the node at %1$y.' => '正在尝试连接到 %1$y 上的节点。', + 'Trying to connect to the prices server at %1$y.' => '正在尝试连接到 %1$y 上的价格服务器。', + 'Type:' => '类型:', + 'Unauthorized' => '未经授权', + 'Unauthorized response from the recipient.' => '收件人未经授权响应。', + 'Unbroadcast transactions can\'t be rebroadcast.' => '未广播的交易无法重新广播。', + 'Unconfirmed' => '未确认', + 'Unconfirmed amount:' => '未确认金额:', + 'Unknown' => '未知', + 'Unlock' => '解锁', + 'Unlocked.' => '已解锁。', + 'Unlock Error' => '解锁错误', + /*TODO*///'Unlock that hardware wallet to continue connecting to it.' => '', + 'Unlock the hardware wallet for %1$y to continue getting the payment proof address.' => '解锁硬件钱包 %1$y 以继续获取付款证明地址。', + 'Unlock the hardware wallet for %1$y to continue mining.' => '解锁硬件钱包 %1$y 以继续挖矿。', + 'Unlock the hardware wallet for %1$y to continue receiving a payment.' => '解锁硬件钱包 %1$y 以继续接收付款。', + 'Unlock the hardware wallet for %1$y to continue sending the payment.' => '解锁硬件钱包 %1$y 以继续发送付款。', + 'Unlock the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => '解锁钱包 %1$s 以继续获取付款证明地址。', + 'Unlock the hardware wallet for Wallet %1$s to continue mining.' => '解锁钱包 %1$s 以继续挖矿。', + 'Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.' => '解锁钱包 %1$s 以继续接收付款。', + 'Unlock the hardware wallet for Wallet %1$s to continue sending the payment.' => '解锁钱包 %1$s 以继续发送付款。', + 'Unsupported response from the recipient.' => '收件人不支持的响应。', + 'Up' => '向上', + 'Update Available' => '可用更新', + 'Update Installed' => '已安装更新', + 'Update your browser to use this feature.' => '请更新您的浏览器以使用此功能。', + 'Updating the prices failed.' => '更新价格失败。', + 'URL' => '网址', + 'Use custom listener' => '使用自定义监听器', + 'Use custom node' => '使用自定义节点', + 'Use custom Tor proxy' => '使用自定义Tor代理', + 'Uses a custom listener instead of the default listener' => '使用自定义监听器而不是默认监听器', + 'Uses a custom node instead of the default nodes' => '使用自定义节点而不是默认节点', + 'Uses a custom Tor proxy instead of the default Tor proxy' => '使用自定义Tor代理而不是默认Tor代理', + 'Utilities' => '实用工具', + 'Value:' => '价值:', + 'Value: %1$c' => '价值:%1$c', + 'Value Copied' => '已复制价值', + 'Value in %1$y' => '价值为 %1$y', + 'Verifying the payment proof address failed.' => '验证付款证明地址失败。', + 'Verifying the Slatepack address on the hardware wallet failed.' => '在硬件钱包上验证 Slatepack 地址失败。', + 'Verifying the Tor address on the hardware wallet failed.' => '在硬件钱包上验证 Tor 地址失败。', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no recipient payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the recipient payment proof address displayed matches the following payment proof address.' => '', + /*TODO*///'Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.' => '', + 'Verify that the node\'s foreign API secret is correct.' => '请验证节点的外部 API 密钥是否正确。', + 'Verify that the pasted address matches the following address when you paste it.' => '请验证粘贴地址时是否与以下地址匹配。', + 'Verify that the pasted ID matches the following ID when you paste it.' => '请验证粘贴 ID 时是否与以下 ID 匹配。', + 'Verify that the pasted value matches the following value when you paste it.' => '请验证粘贴价值时是否与以下价值匹配。', + 'Verify that the Slatepack address displayed on the hardware wallet matches the following payment proof address.' => '验证硬件钱包上显示的Slatepack地址与以下支付凭证地址是否匹配。', + 'Verify that the Tor address displayed on the hardware wallet matches the following payment proof address.' => '验证硬件钱包上显示的Tor地址与以下支付凭证地址是否匹配。', + 'Verify the payment proof address on the hardware wallet for %1$y to continue getting the payment proof address.' => '验证%1$y的硬件钱包上的支付凭证地址以继续获取支付凭证地址。', + 'Verify the payment proof address on the hardware wallet for Wallet %1$s to continue getting the payment proof address.' => '验证“%1$s”钱包的硬件钱包上的支付凭证地址以继续获取支付凭证地址。', + 'Version %1$v' => '版本%1$v', + 'Version Changes' => '版本更改', + 'Version Information' => '版本信息', + 'Version number:' => '版本号:', + 'Wallet %1$s' => '钱包“%1$s”', + 'Wallet Error' => '钱包错误', + 'Wallet Renamed' => '钱包重命名', + 'Wallets' => '钱包', + 'Wallet Settings' => '钱包设置', + 'Wallet type' => '钱包类型', + 'Wallet type:' => '钱包类型:', + 'Website API integration' => '网站API集成', + 'Yes' => '是', + 'You aren\'t connected to a listener.' => '你未连接到监听器。', + /*TODO*///'You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.' => '', + 'You can guarantee that this payment is going to the intended recipient by having the recipient confirm that this payment proof address is their payment proof address.' => '通过让收款方确认此付款证明地址是他们的付款证明地址,您可以确保此付款到达了预期的收款方。', + 'You can only receive payments at this address while you\'re online and connected to a listener.' => '只有在您在线且已连接到监听器时,您才能在此地址接收付款。', + /*TODO*///'You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.' => '', + 'You can\'t guarantee that this payment is going to the intended recipient since the transaction doesn\'t have a payment proof.' => '由于此交易没有付款证明,您无法确保此付款到达预期的收款方。', + /*TODO*///'You\'ll be sending %1$c from %2$y for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from %2$y to the following address for a fee of %3$c.' => '您将从%2$y发送%1$c到以下地址,费用为%3$c。', + /*TODO*///'You\'ll be sending %1$c from Wallet %2$s for a fee of %3$c.' => '', + 'You\'ll be sending %1$c from Wallet %2$s to the following address for a fee of %3$c.' => '您将从钱包%2$s发送%1$c到以下地址,费用为%3$c。', + 'You\'ll need to provide a Tor proxy address to connect to the node.' => '您需要提供一个Tor代理地址才能连接到节点。', + 'You\'ll need to provide a Tor proxy address to connect to the recipient.' => '您需要提供一个Tor代理地址才能连接到收款方。', + 'You\'ll no longer be able to receive payments at the wallet\'s current address once its address suffix has been changed.' => '一旦更改了钱包的地址后缀,您将无法在钱包当前地址接收付款。', + 'You may need to be already paired with the device before this app can connect to it.' => '在此应用程序连接到设备之前,您可能需要先将设备配对。', + 'You may need to be already paired with the device before this extension can connect to it.' => '在此扩展连接到设备之前,您可能需要先将设备配对。', + 'You may need to be already paired with the device before this site can connect to it.' => '在此网站连接到设备之前,您可能需要先将设备配对。', + /*TODO*///'You may need to disconnect and reconnect the hardware wallet to connect to it.' => '', + 'You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.' => '如果扩展无法连接到硬件钱包,则需要在选项卡或窗口中打开此扩展。', + 'You may need to specify a different Tor proxy address to connect to the node.' => '您可能需要指定不同的Tor代理地址才能连接到节点。', + 'You may need to specify a different Tor proxy address to connect to the recipient.' => '您可能需要指定不同的Tor代理地址才能连接到收款方。', + 'You may need to specify a listener address that is served over HTTPS to connect to the listener.' => '您可能需要指定通过HTTPS提供的监听器地址才能连接到监听器。', + 'You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.' => '您可能需要指定通过HTTPS或洋葱服务提供的节点地址才能连接到节点。', + 'You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.' => '您可能需要指定由HTTPS或洋葱网络服务提供的收件人地址才能连接到收件人。', + 'Your browser doesn\'t allow using a camera.' => '您的浏览器不允许使用摄像头。', + 'Your browser doesn\'t allow using USB or Bluetooth devices.' => '您的浏览器不允许使用USB或蓝牙设备。', + 'Your browser doesn\'t support JavaScript. Update your browser and/or enable JavaScript to continue.' => '您的浏览器不支持JavaScript。请更新您的浏览器和/或启用JavaScript以继续。', + 'Your browser isn\'t compatible. Update your browser to continue.' => '您的浏览器不兼容。请更新您的浏览器以继续。', + 'You\'re no longer connected to a node.' => '您已断开与节点的连接。', + 'You\'re no longer connected to the listener.' => '您已断开与监听器的连接。', + 'You\'re no longer connected to the node.' => '您已断开与节点的连接。', + 'You\'re not authorized to connect to the node.' => '您未被授权连接到节点。', + 'You\'re sending %1$c for a fee of %2$c, and the transaction doesn\'t have a payment proof.' => '您正在发送%1$c,手续费为%2$c,此交易没有付款证明。', + 'You\'re sending %1$c for a fee of %2$c, and the transaction\'s recipient payment proof address is the following payment proof address.' => '您正在发送%1$c,手续费为%2$c,收件人的付款证明地址是以下付款证明地址。', + 'Your password was successfully changed.' => '您的密码已成功更改。', + 'You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.' => '在区块链上确认此付款之前,您不应该认为它是合法的。', + 'You were sent %1$c to %2$y.' => '您已发送%1$c给%2$y。', + 'You were sent %1$c to %2$y with a message.' => '您已发送%1$c到%2$y,并带有一条消息。', + 'You were sent %1$c to Wallet %2$s.' => '您已发送%1$c到钱包%2$s。', + 'You were sent %1$c to Wallet %2$s with a message.' => '您已发送%1$c到钱包%2$s,并带有一条消息。', + 'You won\'t be able to change address suffixes without being connected to a listener.' => '如果未连接到监听器,则无法更改地址后缀。', + 'You won\'t be able to rebroadcast transactions without being connected to a node.' => '如果未连接到节点,则无法重新广播交易。', + 'You won\'t be able to receive payments without being connected to a listener.' => '如果未连接到监听器,则无法接收付款。', + 'You won\'t be able to send payments without being connected to a node.' => '如果未连接到节点,则无法发送付款。', + ] + ]; +?> diff --git a/models/mwc.json b/models/mwc.json new file mode 100755 index 0000000..cfce636 --- /dev/null +++ b/models/mwc.json @@ -0,0 +1,13520 @@ +{ + "Textures": [ + "data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAABHNCSVQICAgIfAhkiAAAABVJREFUCNdj\/A8BDEwMDAwMjIyMDABh3QcAwI5gVQAAAABJRU5ErkJggg==" + ], + "Textured Models": [ + { + "Minimum Corner": [ + -12.01846, + -6.059581, + -0.575 + ], + "Maximum Corner": [ + 12.01846, + 6.059581, + 0.575 + ], + "Shininess": 10, + "Emission Texture Index": -1, + "Normal Texture Index": -1, + "Diffuse Texture Index": 0, + "Indices": [ + 0, + 1, + 2, + 1, + 3, + 2, + 3, + 4, + 2, + 4, + 5, + 2, + 5, + 6, + 2, + 6, + 7, + 2, + 7, + 8, + 2, + 8, + 9, + 2, + 9, + 10, + 2, + 10, + 11, + 2, + 11, + 12, + 2, + 12, + 13, + 2, + 13, + 14, + 2, + 14, + 15, + 2, + 15, + 16, + 2, + 16, + 17, + 2, + 17, + 18, + 2, + 18, + 19, + 2, + 19, + 20, + 2, + 20, + 21, + 2, + 21, + 22, + 2, + 22, + 23, + 2, + 23, + 24, + 2, + 24, + 25, + 2, + 25, + 26, + 2, + 26, + 27, + 2, + 27, + 28, + 2, + 2, + 28, + 29, + 28, + 30, + 29, + 29, + 30, + 31, + 30, + 32, + 31, + 32, + 33, + 31, + 33, + 34, + 31, + 34, + 35, + 31, + 35, + 36, + 31, + 36, + 37, + 31, + 37, + 38, + 31, + 38, + 39, + 31, + 39, + 40, + 31, + 40, + 41, + 31, + 41, + 42, + 31, + 42, + 43, + 31, + 43, + 44, + 31, + 44, + 45, + 31, + 45, + 46, + 31, + 46, + 47, + 31, + 47, + 48, + 31, + 48, + 49, + 31, + 49, + 50, + 31, + 50, + 51, + 31, + 51, + 52, + 31, + 52, + 53, + 31, + 53, + 54, + 31, + 54, + 55, + 31, + 55, + 56, + 31, + 56, + 57, + 31, + 57, + 58, + 31, + 58, + 59, + 31, + 59, + 60, + 31, + 60, + 61, + 31, + 61, + 62, + 31, + 62, + 63, + 31, + 63, + 64, + 31, + 64, + 65, + 31, + 65, + 66, + 31, + 66, + 67, + 31, + 67, + 68, + 31, + 68, + 69, + 31, + 69, + 70, + 31, + 70, + 71, + 31, + 71, + 72, + 31, + 72, + 73, + 31, + 73, + 74, + 31, + 74, + 75, + 31, + 75, + 76, + 31, + 76, + 77, + 31, + 77, + 78, + 31, + 78, + 79, + 31, + 79, + 80, + 31, + 80, + 81, + 31, + 81, + 82, + 31, + 31, + 82, + 83, + 82, + 84, + 83, + 83, + 84, + 85, + 84, + 86, + 85, + 85, + 86, + 87, + 86, + 88, + 87, + 87, + 88, + 89, + 88, + 90, + 89, + 89, + 90, + 91, + 90, + 92, + 91, + 91, + 92, + 93, + 92, + 94, + 93, + 93, + 94, + 95, + 94, + 96, + 95, + 95, + 96, + 97, + 96, + 98, + 97, + 97, + 98, + 99, + 98, + 100, + 99, + 100, + 101, + 99, + 99, + 101, + 102, + 101, + 103, + 102, + 102, + 103, + 104, + 103, + 105, + 104, + 104, + 105, + 106, + 105, + 107, + 106, + 106, + 107, + 108, + 107, + 109, + 108, + 108, + 109, + 110, + 109, + 111, + 110, + 110, + 111, + 112, + 111, + 113, + 112, + 112, + 113, + 114, + 113, + 115, + 114, + 114, + 115, + 116, + 115, + 117, + 116, + 116, + 117, + 118, + 117, + 119, + 118, + 118, + 119, + 120, + 119, + 121, + 120, + 120, + 121, + 122, + 121, + 123, + 122, + 122, + 123, + 124, + 123, + 125, + 124, + 124, + 125, + 126, + 125, + 127, + 126, + 126, + 127, + 128, + 127, + 129, + 128, + 128, + 129, + 130, + 129, + 131, + 130, + 130, + 131, + 132, + 131, + 133, + 132, + 132, + 133, + 134, + 133, + 135, + 134, + 134, + 135, + 136, + 135, + 137, + 136, + 137, + 138, + 136, + 138, + 139, + 136, + 139, + 140, + 136, + 140, + 141, + 136, + 141, + 142, + 136, + 142, + 143, + 136, + 143, + 144, + 136, + 144, + 145, + 136, + 145, + 146, + 136, + 146, + 147, + 136, + 147, + 148, + 136, + 148, + 149, + 136, + 149, + 150, + 136, + 150, + 151, + 136, + 151, + 152, + 136, + 152, + 153, + 136, + 153, + 154, + 136, + 154, + 155, + 136, + 155, + 156, + 136, + 136, + 156, + 157, + 157, + 156, + 158, + 158, + 156, + 159, + 159, + 156, + 160, + 160, + 156, + 161, + 161, + 156, + 162, + 162, + 156, + 163, + 163, + 156, + 164, + 164, + 156, + 165, + 165, + 156, + 166, + 166, + 156, + 167, + 167, + 156, + 168, + 168, + 156, + 169, + 169, + 156, + 170, + 170, + 156, + 171, + 171, + 156, + 172, + 172, + 156, + 173, + 173, + 156, + 174, + 174, + 156, + 175, + 175, + 156, + 176, + 156, + 177, + 176, + 177, + 178, + 176, + 178, + 179, + 176, + 179, + 180, + 176, + 180, + 181, + 176, + 181, + 182, + 176, + 182, + 183, + 176, + 183, + 184, + 176, + 184, + 185, + 176, + 185, + 186, + 176, + 186, + 187, + 176, + 187, + 188, + 176, + 188, + 189, + 176, + 189, + 190, + 176, + 190, + 191, + 176, + 191, + 192, + 176, + 192, + 193, + 176, + 193, + 194, + 176, + 194, + 195, + 176, + 196, + 197, + 198, + 198, + 197, + 199, + 197, + 200, + 199, + 199, + 200, + 201, + 200, + 202, + 201, + 201, + 202, + 203, + 202, + 204, + 203, + 203, + 204, + 205, + 204, + 206, + 205, + 205, + 206, + 207, + 206, + 208, + 207, + 207, + 208, + 209, + 208, + 210, + 209, + 209, + 210, + 211, + 210, + 212, + 211, + 211, + 212, + 213, + 212, + 214, + 213, + 213, + 214, + 215, + 214, + 216, + 215, + 215, + 216, + 217, + 216, + 218, + 217, + 217, + 218, + 219, + 218, + 220, + 219, + 219, + 220, + 221, + 220, + 222, + 221, + 221, + 222, + 223, + 222, + 224, + 223, + 223, + 224, + 225, + 224, + 226, + 225, + 225, + 226, + 227, + 226, + 228, + 227, + 227, + 228, + 229, + 228, + 230, + 229, + 229, + 230, + 231, + 230, + 232, + 231, + 231, + 232, + 233, + 232, + 234, + 233, + 233, + 234, + 235, + 234, + 236, + 235, + 235, + 236, + 237, + 236, + 238, + 237, + 237, + 238, + 239, + 238, + 240, + 239, + 239, + 240, + 241, + 240, + 242, + 241, + 241, + 242, + 243, + 242, + 244, + 243, + 243, + 244, + 245, + 244, + 246, + 245, + 245, + 246, + 247, + 246, + 248, + 247, + 247, + 248, + 249, + 248, + 250, + 249, + 249, + 250, + 251, + 252, + 253, + 254, + 254, + 253, + 255, + 253, + 256, + 255, + 255, + 256, + 257, + 256, + 258, + 257, + 257, + 258, + 259, + 258, + 260, + 259, + 259, + 260, + 261, + 262, + 263, + 264, + 264, + 263, + 265, + 263, + 266, + 265, + 265, + 266, + 267, + 266, + 268, + 267, + 267, + 268, + 269, + 268, + 270, + 269, + 269, + 270, + 271, + 270, + 272, + 271, + 271, + 272, + 273, + 272, + 274, + 273, + 273, + 274, + 275, + 274, + 276, + 275, + 275, + 276, + 277, + 276, + 278, + 277, + 277, + 278, + 279, + 278, + 280, + 279, + 279, + 280, + 281, + 280, + 282, + 281, + 281, + 282, + 283, + 282, + 284, + 283, + 283, + 284, + 285, + 284, + 286, + 285, + 285, + 286, + 287, + 286, + 288, + 287, + 287, + 288, + 289, + 288, + 290, + 289, + 289, + 290, + 291, + 290, + 292, + 291, + 291, + 292, + 293, + 292, + 294, + 293, + 293, + 294, + 295, + 294, + 296, + 295, + 295, + 296, + 297, + 296, + 298, + 297, + 297, + 298, + 299, + 298, + 300, + 299, + 299, + 300, + 301, + 300, + 302, + 301, + 301, + 302, + 303, + 302, + 304, + 303, + 303, + 304, + 305, + 306, + 307, + 308, + 308, + 307, + 309, + 307, + 310, + 309, + 309, + 310, + 311, + 310, + 312, + 311, + 311, + 312, + 313, + 314, + 315, + 316, + 316, + 315, + 317, + 315, + 318, + 317, + 317, + 318, + 319, + 318, + 320, + 319, + 319, + 320, + 321, + 320, + 322, + 321, + 321, + 322, + 323, + 322, + 324, + 323, + 323, + 324, + 325, + 324, + 326, + 325, + 325, + 326, + 327, + 326, + 328, + 327, + 327, + 328, + 329, + 328, + 330, + 329, + 329, + 330, + 331, + 330, + 332, + 331, + 331, + 332, + 333, + 332, + 334, + 333, + 333, + 334, + 335, + 334, + 336, + 335, + 335, + 336, + 337, + 336, + 338, + 337, + 337, + 338, + 339, + 338, + 340, + 339, + 339, + 340, + 341, + 340, + 342, + 341, + 341, + 342, + 343, + 342, + 344, + 343, + 343, + 344, + 345, + 344, + 346, + 345, + 345, + 346, + 347, + 346, + 348, + 347, + 347, + 348, + 349, + 348, + 350, + 349, + 349, + 350, + 351, + 350, + 352, + 351, + 351, + 352, + 353, + 352, + 354, + 353, + 353, + 354, + 355, + 354, + 356, + 355, + 355, + 356, + 357, + 356, + 358, + 357, + 357, + 358, + 359, + 358, + 360, + 359, + 359, + 360, + 361, + 360, + 362, + 361, + 361, + 362, + 363, + 362, + 364, + 363, + 363, + 364, + 365, + 364, + 366, + 365, + 365, + 366, + 367, + 366, + 368, + 367, + 367, + 368, + 369, + 370, + 371, + 308, + 308, + 371, + 306, + 372, + 373, + 370, + 370, + 373, + 371, + 374, + 375, + 372, + 372, + 375, + 373, + 376, + 377, + 374, + 374, + 377, + 375, + 378, + 379, + 376, + 376, + 379, + 377, + 380, + 381, + 378, + 378, + 381, + 379, + 382, + 383, + 380, + 380, + 383, + 381, + 305, + 304, + 382, + 382, + 304, + 383, + 384, + 385, + 264, + 264, + 385, + 262, + 386, + 387, + 384, + 384, + 387, + 385, + 388, + 389, + 386, + 386, + 389, + 387, + 390, + 391, + 388, + 388, + 391, + 389, + 392, + 393, + 390, + 390, + 393, + 391, + 394, + 395, + 392, + 392, + 395, + 393, + 396, + 397, + 394, + 394, + 397, + 395, + 261, + 260, + 396, + 396, + 260, + 397, + 398, + 399, + 400, + 400, + 399, + 401, + 400, + 401, + 402, + 402, + 401, + 403, + 402, + 403, + 404, + 404, + 403, + 405, + 404, + 405, + 406, + 406, + 405, + 407, + 406, + 407, + 408, + 408, + 407, + 409, + 408, + 409, + 410, + 410, + 409, + 411, + 410, + 411, + 412, + 412, + 411, + 413, + 412, + 413, + 414, + 414, + 413, + 415, + 414, + 415, + 416, + 416, + 415, + 417, + 416, + 417, + 418, + 418, + 417, + 419, + 418, + 419, + 420, + 420, + 419, + 421, + 420, + 421, + 422, + 422, + 421, + 423, + 422, + 423, + 424, + 424, + 423, + 425, + 424, + 425, + 426, + 426, + 425, + 427, + 426, + 427, + 428, + 428, + 427, + 429, + 428, + 429, + 430, + 430, + 429, + 431, + 430, + 431, + 432, + 432, + 431, + 433, + 432, + 433, + 434, + 434, + 433, + 435, + 434, + 435, + 436, + 436, + 435, + 437, + 436, + 437, + 438, + 438, + 437, + 439, + 438, + 439, + 440, + 440, + 439, + 441, + 440, + 441, + 442, + 442, + 441, + 443, + 442, + 443, + 444, + 444, + 443, + 445, + 444, + 445, + 446, + 446, + 445, + 447, + 446, + 447, + 448, + 448, + 447, + 449, + 448, + 449, + 450, + 450, + 449, + 451, + 450, + 451, + 452, + 452, + 451, + 453, + 454, + 455, + 456, + 456, + 455, + 457, + 456, + 457, + 458, + 458, + 457, + 459, + 458, + 459, + 460, + 460, + 459, + 461, + 460, + 461, + 462, + 462, + 461, + 463, + 464, + 465, + 466, + 466, + 465, + 467, + 466, + 467, + 468, + 468, + 467, + 469, + 468, + 469, + 470, + 470, + 469, + 471, + 470, + 471, + 472, + 472, + 471, + 473, + 472, + 473, + 474, + 474, + 473, + 475, + 474, + 475, + 476, + 476, + 475, + 477, + 476, + 477, + 478, + 478, + 477, + 479, + 478, + 479, + 480, + 480, + 479, + 481, + 480, + 481, + 482, + 482, + 481, + 483, + 482, + 483, + 484, + 484, + 483, + 485, + 484, + 485, + 486, + 486, + 485, + 487, + 486, + 487, + 488, + 488, + 487, + 489, + 488, + 489, + 490, + 490, + 489, + 491, + 490, + 491, + 492, + 492, + 491, + 493, + 492, + 493, + 494, + 494, + 493, + 495, + 494, + 495, + 496, + 496, + 495, + 497, + 496, + 497, + 498, + 498, + 497, + 499, + 498, + 499, + 500, + 500, + 499, + 501, + 500, + 501, + 502, + 502, + 501, + 503, + 502, + 503, + 504, + 504, + 503, + 505, + 504, + 505, + 506, + 506, + 505, + 507, + 508, + 509, + 510, + 510, + 509, + 511, + 510, + 511, + 512, + 512, + 511, + 513, + 512, + 513, + 514, + 514, + 513, + 515, + 516, + 517, + 518, + 518, + 517, + 519, + 518, + 519, + 520, + 520, + 519, + 521, + 520, + 521, + 522, + 522, + 521, + 523, + 522, + 523, + 524, + 524, + 523, + 525, + 524, + 525, + 526, + 526, + 525, + 527, + 526, + 527, + 528, + 528, + 527, + 529, + 528, + 529, + 530, + 530, + 529, + 531, + 530, + 531, + 532, + 532, + 531, + 533, + 532, + 533, + 534, + 534, + 533, + 535, + 534, + 535, + 536, + 536, + 535, + 537, + 536, + 537, + 538, + 538, + 537, + 539, + 538, + 539, + 540, + 540, + 539, + 541, + 540, + 541, + 542, + 542, + 541, + 543, + 542, + 543, + 544, + 544, + 543, + 545, + 544, + 545, + 546, + 546, + 545, + 547, + 546, + 547, + 548, + 548, + 547, + 549, + 548, + 549, + 550, + 550, + 549, + 551, + 550, + 551, + 552, + 552, + 551, + 553, + 552, + 553, + 554, + 554, + 553, + 555, + 554, + 555, + 556, + 556, + 555, + 557, + 556, + 557, + 558, + 558, + 557, + 559, + 558, + 559, + 560, + 560, + 559, + 561, + 560, + 561, + 562, + 562, + 561, + 563, + 562, + 563, + 564, + 564, + 563, + 565, + 564, + 565, + 566, + 566, + 565, + 567, + 566, + 567, + 568, + 568, + 567, + 569, + 568, + 569, + 570, + 570, + 569, + 571, + 572, + 509, + 573, + 573, + 509, + 508, + 574, + 572, + 575, + 575, + 572, + 573, + 576, + 574, + 577, + 577, + 574, + 575, + 578, + 576, + 579, + 579, + 576, + 577, + 580, + 578, + 581, + 581, + 578, + 579, + 582, + 580, + 583, + 583, + 580, + 581, + 584, + 582, + 585, + 585, + 582, + 583, + 507, + 584, + 506, + 506, + 584, + 585, + 586, + 465, + 587, + 587, + 465, + 464, + 588, + 586, + 589, + 589, + 586, + 587, + 590, + 588, + 591, + 591, + 588, + 589, + 592, + 590, + 593, + 593, + 590, + 591, + 594, + 592, + 595, + 595, + 592, + 593, + 596, + 594, + 597, + 597, + 594, + 595, + 598, + 596, + 599, + 599, + 596, + 597, + 463, + 598, + 462, + 462, + 598, + 599, + 600, + 601, + 602, + 601, + 603, + 602, + 603, + 604, + 602, + 604, + 605, + 602, + 605, + 606, + 602, + 606, + 607, + 602, + 607, + 608, + 602, + 608, + 609, + 602, + 609, + 610, + 602, + 610, + 611, + 602, + 611, + 612, + 602, + 612, + 613, + 602, + 613, + 614, + 602, + 614, + 615, + 602, + 615, + 616, + 602, + 616, + 617, + 602, + 617, + 618, + 602, + 618, + 619, + 602, + 619, + 620, + 602, + 620, + 621, + 602, + 621, + 622, + 602, + 622, + 623, + 602, + 623, + 624, + 602, + 624, + 625, + 602, + 625, + 626, + 602, + 626, + 627, + 602, + 627, + 628, + 602, + 602, + 628, + 629, + 628, + 630, + 629, + 629, + 630, + 631, + 630, + 632, + 631, + 632, + 633, + 631, + 633, + 634, + 631, + 634, + 635, + 631, + 635, + 636, + 631, + 636, + 637, + 631, + 637, + 638, + 631, + 638, + 639, + 631, + 639, + 640, + 631, + 640, + 641, + 631, + 641, + 642, + 631, + 642, + 643, + 631, + 643, + 644, + 631, + 644, + 645, + 631, + 645, + 646, + 631, + 646, + 647, + 631, + 647, + 648, + 631, + 648, + 649, + 631, + 649, + 650, + 631, + 650, + 651, + 631, + 651, + 652, + 631, + 652, + 653, + 631, + 653, + 654, + 631, + 654, + 655, + 631, + 655, + 656, + 631, + 656, + 657, + 631, + 657, + 658, + 631, + 658, + 659, + 631, + 659, + 660, + 631, + 660, + 661, + 631, + 661, + 662, + 631, + 662, + 663, + 631, + 663, + 664, + 631, + 664, + 665, + 631, + 665, + 666, + 631, + 666, + 667, + 631, + 667, + 668, + 631, + 668, + 669, + 631, + 669, + 670, + 631, + 670, + 671, + 631, + 671, + 672, + 631, + 672, + 673, + 631, + 673, + 674, + 631, + 674, + 675, + 631, + 675, + 676, + 631, + 676, + 677, + 631, + 677, + 678, + 631, + 678, + 679, + 631, + 679, + 680, + 631, + 680, + 681, + 631, + 681, + 682, + 631, + 631, + 682, + 683, + 682, + 684, + 683, + 683, + 684, + 685, + 684, + 686, + 685, + 685, + 686, + 687, + 686, + 688, + 687, + 687, + 688, + 689, + 688, + 690, + 689, + 689, + 690, + 691, + 690, + 692, + 691, + 691, + 692, + 693, + 692, + 694, + 693, + 693, + 694, + 695, + 694, + 696, + 695, + 695, + 696, + 697, + 696, + 698, + 697, + 697, + 698, + 699, + 698, + 700, + 699, + 700, + 701, + 699, + 699, + 701, + 702, + 701, + 703, + 702, + 702, + 703, + 704, + 703, + 705, + 704, + 704, + 705, + 706, + 705, + 707, + 706, + 706, + 707, + 708, + 707, + 709, + 708, + 708, + 709, + 710, + 709, + 711, + 710, + 710, + 711, + 712, + 711, + 713, + 712, + 712, + 713, + 714, + 713, + 715, + 714, + 714, + 715, + 716, + 715, + 717, + 716, + 716, + 717, + 718, + 717, + 719, + 718, + 718, + 719, + 720, + 719, + 721, + 720, + 720, + 721, + 722, + 721, + 723, + 722, + 722, + 723, + 724, + 723, + 725, + 724, + 724, + 725, + 726, + 725, + 727, + 726, + 726, + 727, + 728, + 727, + 729, + 728, + 728, + 729, + 730, + 729, + 731, + 730, + 730, + 731, + 732, + 731, + 733, + 732, + 732, + 733, + 734, + 733, + 735, + 734, + 734, + 735, + 736, + 735, + 737, + 736, + 737, + 738, + 736, + 738, + 739, + 736, + 739, + 740, + 736, + 740, + 741, + 736, + 741, + 742, + 736, + 742, + 743, + 736, + 743, + 744, + 736, + 744, + 745, + 736, + 745, + 746, + 736, + 746, + 747, + 736, + 747, + 748, + 736, + 748, + 749, + 736, + 749, + 750, + 736, + 750, + 751, + 736, + 751, + 752, + 736, + 752, + 753, + 736, + 753, + 754, + 736, + 754, + 755, + 736, + 755, + 756, + 736, + 736, + 756, + 757, + 757, + 756, + 758, + 758, + 756, + 759, + 759, + 756, + 760, + 760, + 756, + 761, + 761, + 756, + 762, + 762, + 756, + 763, + 763, + 756, + 764, + 764, + 756, + 765, + 765, + 756, + 766, + 766, + 756, + 767, + 767, + 756, + 768, + 768, + 756, + 769, + 769, + 756, + 770, + 770, + 756, + 771, + 771, + 756, + 772, + 772, + 756, + 773, + 773, + 756, + 774, + 774, + 756, + 775, + 775, + 756, + 776, + 756, + 777, + 776, + 777, + 778, + 776, + 778, + 779, + 776, + 779, + 780, + 776, + 780, + 781, + 776, + 781, + 782, + 776, + 782, + 783, + 776, + 783, + 784, + 776, + 784, + 785, + 776, + 785, + 786, + 776, + 786, + 787, + 776, + 787, + 788, + 776, + 788, + 789, + 776, + 789, + 790, + 776, + 790, + 791, + 776, + 791, + 792, + 776, + 792, + 793, + 776, + 793, + 794, + 776, + 794, + 795, + 776 + ], + "Bitangents": [ + 0.999955, + -0.009504, + 0, + 0.999993, + -0.003849, + 0, + 1, + -0.000248, + 0, + 0.999979, + 0.006467, + 0, + 0.999996, + 0.002945, + 0, + 1, + 0.000067, + 0, + 1, + 0.000286, + 0, + 1, + -0.000039, + 0, + 1, + -0.000113, + 0, + 1, + -0.000243, + 0, + 1, + -0.000035, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + 0.000002, + 0, + 1, + 0.000006, + 0, + 1, + 0.000002, + 0, + 1, + -0.000003, + 0, + 1, + 0.000002, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000002, + 0, + 1, + -0.000002, + 0, + 1, + 0.000002, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0.000002, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000004, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + 0.000003, + 0, + 1, + 0.000002, + 0, + 1, + -0.000004, + 0, + 1, + -0.000008, + 0, + 1, + -0.000003, + 0, + 1, + 0.00001, + 0, + 1, + 0.000006, + 0, + 1, + 0.000004, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0.000002, + 0, + 1, + -0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0.000005, + 0, + 1, + -0.000001, + 0, + 1, + 0.000009, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + -0.000006, + 0, + 1, + -0.000009, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0.000009, + 0, + 1, + 0.000004, + 0, + 1, + -0.000005, + 0, + 1, + -0.000003, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0.000003, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + -0.000002, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0.000003, + 0, + 1, + -0.000008, + 0, + 1, + -0.000006, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000002, + 0, + 1, + -0.000002, + 0, + 1, + 0.000003, + 0, + 1, + -0.000003, + 0, + 1, + -0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0.000003, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0.000005, + 0, + 1, + 0.000006, + 0, + 1, + -0.000006, + 0, + 1, + -0.000002, + 0, + 1, + 0.000009, + 0, + 1, + -0.000004, + 0, + 1, + 0.000004, + 0, + 1, + -0, + 0, + 1, + -0.000003, + 0, + 1, + -0.000002, + 0, + 1, + -0.000009, + 0, + 1, + -0.000015, + 0, + 1, + 0.000003, + 0, + 1, + 0.000003, + 0, + 1, + -0.000006, + 0, + 1, + 0.000013, + 0, + 1, + -0.000025, + 0, + 1, + -0.000013, + 0, + 1, + 0.000023, + 0, + 1, + 0, + -0, + 0.992634, + -0.121153, + 0, + 1, + 0, + -0, + 0.992634, + -0.121153, + 0, + 0.983919, + -0.178614, + 0, + 0.983919, + -0.178614, + 0, + 0.96626, + -0.25757, + 0, + 0.96626, + -0.25757, + 0, + 0.942508, + -0.334182, + 0, + 0.942508, + -0.334182, + 0, + 0.912986, + -0.40799, + 0, + 0.912986, + -0.40799, + 0, + 0.878021, + -0.478622, + 0, + 0.878021, + -0.478622, + 0, + 0.837953, + -0.545743, + 0, + 0.837953, + -0.545743, + 0, + 0.793106, + -0.609083, + 0, + 0.793106, + -0.609083, + 0, + 0.743801, + -0.668402, + 0, + 0.743801, + -0.668402, + 0, + 0.710648, + -0.703548, + 0, + 0.710648, + -0.703548, + 0, + 0.704325, + -0.709878, + 0, + 0.704325, + -0.709878, + 0, + 0.704336, + -0.709867, + 0, + 0.704336, + -0.709867, + 0, + 0.70435, + -0.709853, + 0, + 0.70435, + -0.709853, + 0, + 0.704368, + -0.709835, + 0, + 0.704368, + -0.709835, + 0, + 0.704392, + -0.709811, + 0, + 0.704392, + -0.709811, + 0, + 0.704424, + -0.70978, + 0, + 0.704424, + -0.70978, + 0, + 0.704469, + -0.709735, + 0, + 0.704469, + -0.709735, + 0, + 0.704538, + -0.709667, + 0, + 0.704538, + -0.709667, + 0, + 0.704655, + -0.70955, + 0, + 0.704655, + -0.70955, + 0, + 0.738079, + -0.674714, + 0, + 0.738079, + -0.674714, + 0, + 0.844893, + -0.534936, + 0, + 0.844893, + -0.534936, + 0, + 0.95014, + -0.311823, + 0, + 0.95014, + -0.311823, + 0, + 0.996639, + -0.081916, + 0, + 0.996639, + -0.081916, + 0, + 0.99284, + 0.119452, + 0, + 0.99284, + 0.119452, + 0, + 0.960496, + 0.278294, + 0, + 0.960496, + 0.278294, + 0, + 0.917584, + 0.397542, + 0, + 0.917584, + 0.397542, + 0, + 0.874097, + 0.485751, + 0, + 0.874097, + 0.485751, + 0, + -0.874097, + -0.485751, + 0, + -0.83472, + -0.550675, + 0, + -0.874097, + -0.485751, + 0, + -0.83472, + -0.550675, + 0, + -0.80334, + -0.59552, + 0, + -0.80334, + -0.59552, + 0, + -0.709166, + -0.705042, + 0, + -0.709166, + -0.705042, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704927, + -0.709279, + 0, + -0.673636, + -0.739063, + 0, + -0.704928, + -0.709279, + 0, + -0.673636, + -0.739063, + 0, + -0.605536, + -0.795818, + 0, + -0.605536, + -0.795818, + 0, + -0.5339, + -0.845548, + 0, + -0.5339, + -0.845548, + 0, + -0.45931, + -0.888276, + 0, + -0.45931, + -0.888276, + 0, + -0.382203, + -0.924078, + 0, + -0.382203, + -0.924078, + 0, + -0.302919, + -0.953016, + 0, + -0.302919, + -0.953016, + 0, + -0.221722, + -0.97511, + 0, + -0.221722, + -0.97511, + 0, + -0.138822, + -0.990317, + 0, + -0.138822, + -0.990317, + 0, + -0.054453, + -0.998516, + 0, + -0.054453, + -0.998516, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0.054453, + -0.998516, + 0, + 0.054453, + -0.998516, + 0, + 0.138822, + -0.990317, + 0, + 0.138822, + -0.990317, + 0, + 0.221722, + -0.97511, + 0, + 0.221722, + -0.97511, + 0, + 0.302919, + -0.953016, + 0, + 0.302919, + -0.953016, + 0, + 0.382203, + -0.924078, + 0, + 0.382203, + -0.924078, + 0, + 0.45931, + -0.888276, + 0, + 0.45931, + -0.888276, + 0, + 0.5339, + -0.845548, + 0, + 0.5339, + -0.845548, + 0, + 0.605536, + -0.795818, + 0, + 0.605536, + -0.795818, + 0, + 0.673636, + -0.739063, + 0, + 0.673636, + -0.739063, + 0, + 0.704927, + -0.70928, + 0, + 0.704927, + -0.70928, + 0, + 0.70477, + -0.709436, + 0, + 0.70477, + -0.709436, + 0, + 0.709273, + -0.704934, + 0, + 0.805117, + -0.593116, + 0, + 0.709273, + -0.704934, + 0, + 0.805117, + -0.593116, + 0, + 0.836303, + -0.548268, + 0, + 0.836303, + -0.548268, + 0, + 0.875386, + -0.483425, + 0, + 0.875386, + -0.483425, + 0, + 0.875386, + -0.483425, + 0, + 0.918489, + -0.395448, + 0, + 0.875386, + -0.483425, + 0, + 0.918489, + -0.395448, + 0, + 0.96096, + -0.276686, + 0, + 0.96096, + -0.276686, + 0, + 0.992928, + -0.118717, + 0, + 0.992928, + -0.118717, + 0, + 0.996681, + 0.081407, + 0, + 0.996681, + 0.081407, + 0, + 0.950716, + 0.310062, + 0, + 0.950716, + 0.310062, + 0, + 0.846403, + 0.532543, + 0, + 0.846403, + 0.532543, + 0, + 0.739773, + 0.672856, + 0, + 0.739773, + 0.672856, + 0, + 0.705695, + 0.708515, + 0, + 0.705695, + 0.708515, + 0, + 0.704984, + 0.709224, + 0, + 0.704984, + 0.709224, + 0, + 0.704567, + 0.709638, + 0, + 0.704567, + 0.709638, + 0, + 0.704293, + 0.709909, + 0, + 0.704293, + 0.709909, + 0, + 0.7041, + 0.710101, + 0, + 0.7041, + 0.710101, + 0, + 0.703956, + 0.710244, + 0, + 0.703956, + 0.710244, + 0, + 0.703846, + 0.710352, + 0, + 0.703846, + 0.710352, + 0, + 0.703761, + 0.710437, + 0, + 0.703761, + 0.710437, + 0, + 0.703697, + 0.7105, + 0, + 0.703697, + 0.7105, + 0, + 0.710296, + 0.703903, + 0, + 0.710296, + 0.703903, + 0, + 0.743801, + 0.668401, + 0, + 0.743801, + 0.668401, + 0, + 0.793106, + 0.609084, + 0, + 0.793106, + 0.609084, + 0, + 0.837953, + 0.545743, + 0, + 0.837953, + 0.545743, + 0, + 0.878022, + 0.478621, + 0, + 0.878022, + 0.478621, + 0, + 0.912985, + 0.407992, + 0, + 0.912985, + 0.407992, + 0, + 0.94251, + 0.334177, + 0, + 0.94251, + 0.334177, + 0, + 0.966261, + 0.257564, + 0, + 0.966261, + 0.257564, + 0, + 0.983919, + 0.178614, + 0, + 0.983919, + 0.178614, + 0, + 0.992634, + 0.121152, + 0, + 0.992634, + 0.121152, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709436, + 0, + 0.704769, + -0.709436, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709436, + 0, + 0.704769, + -0.709437, + 0, + 0.70477, + -0.709436, + 0, + 0.70477, + -0.709436, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.70477, + -0.709436, + 0, + -0.70477, + -0.709436, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.70477, + -0.709436, + 0, + -0.70477, + -0.709436, + 0, + -1, + 0, + -0, + -1, + 0, + -0, + -0.992634, + -0.121153, + 0, + -0.992634, + -0.121153, + 0, + -0.983919, + -0.178614, + 0, + -0.983919, + -0.178614, + 0, + -0.96626, + -0.25757, + 0, + -0.96626, + -0.25757, + 0, + -0.942508, + -0.334182, + 0, + -0.942508, + -0.334182, + 0, + -0.912986, + -0.40799, + 0, + -0.912986, + -0.40799, + 0, + -0.878021, + -0.478622, + 0, + -0.878021, + -0.478622, + 0, + -0.837953, + -0.545743, + 0, + -0.837953, + -0.545743, + 0, + -0.793106, + -0.609083, + 0, + -0.793106, + -0.609083, + 0, + -0.743801, + -0.668402, + 0, + -0.743801, + -0.668402, + 0, + -0.710648, + -0.703548, + 0, + -0.710648, + -0.703548, + 0, + -0.704325, + -0.709878, + 0, + -0.704325, + -0.709878, + 0, + -0.704336, + -0.709867, + 0, + -0.704336, + -0.709867, + 0, + -0.70435, + -0.709853, + 0, + -0.70435, + -0.709853, + 0, + -0.704368, + -0.709835, + 0, + -0.704368, + -0.709835, + 0, + -0.704392, + -0.709811, + 0, + -0.704392, + -0.709811, + 0, + -0.704424, + -0.70978, + 0, + -0.704424, + -0.70978, + 0, + -0.704469, + -0.709735, + 0, + -0.704469, + -0.709735, + 0, + -0.704538, + -0.709667, + 0, + -0.704538, + -0.709667, + 0, + -0.704655, + -0.70955, + 0, + -0.704655, + -0.70955, + 0, + -0.738079, + -0.674714, + 0, + -0.738079, + -0.674714, + 0, + -0.844893, + -0.534936, + 0, + -0.844893, + -0.534936, + 0, + -0.95014, + -0.311823, + 0, + -0.95014, + -0.311823, + 0, + -0.996639, + -0.081916, + 0, + -0.996639, + -0.081916, + 0, + -0.99284, + 0.119452, + 0, + -0.99284, + 0.119452, + 0, + -0.960496, + 0.278294, + 0, + -0.960496, + 0.278294, + 0, + -0.917584, + 0.397542, + 0, + -0.917584, + 0.397542, + 0, + -0.874097, + 0.485751, + 0, + -0.874097, + 0.485751, + 0, + 0.874097, + -0.485751, + 0, + 0.874097, + -0.485751, + 0, + 0.83472, + -0.550675, + 0, + 0.83472, + -0.550675, + 0, + 0.80334, + -0.59552, + 0, + 0.80334, + -0.59552, + 0, + 0.709166, + -0.705042, + 0, + 0.709166, + -0.705042, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704927, + -0.709279, + 0, + 0.704928, + -0.709279, + 0, + 0.673636, + -0.739063, + 0, + 0.673636, + -0.739063, + 0, + 0.605536, + -0.795818, + 0, + 0.605536, + -0.795818, + 0, + 0.5339, + -0.845548, + 0, + 0.5339, + -0.845548, + 0, + 0.45931, + -0.888276, + 0, + 0.45931, + -0.888276, + 0, + 0.382203, + -0.924078, + 0, + 0.382203, + -0.924078, + 0, + 0.302919, + -0.953016, + 0, + 0.302919, + -0.953016, + 0, + 0.221722, + -0.97511, + 0, + 0.221722, + -0.97511, + 0, + 0.138822, + -0.990317, + 0, + 0.138822, + -0.990317, + 0, + 0.054453, + -0.998516, + 0, + 0.054453, + -0.998516, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0.054453, + -0.998516, + 0, + -0.054453, + -0.998516, + 0, + -0.138822, + -0.990317, + 0, + -0.138822, + -0.990317, + 0, + -0.221722, + -0.97511, + 0, + -0.221722, + -0.97511, + 0, + -0.302919, + -0.953016, + 0, + -0.302919, + -0.953016, + 0, + -0.382203, + -0.924078, + 0, + -0.382203, + -0.924078, + 0, + -0.45931, + -0.888276, + 0, + -0.45931, + -0.888276, + 0, + -0.5339, + -0.845548, + 0, + -0.5339, + -0.845548, + 0, + -0.605536, + -0.795818, + 0, + -0.605536, + -0.795818, + 0, + -0.673636, + -0.739063, + 0, + -0.673636, + -0.739063, + 0, + -0.704927, + -0.70928, + 0, + -0.704927, + -0.70928, + 0, + -0.70477, + -0.709436, + 0, + -0.70477, + -0.709436, + 0, + -0.709273, + -0.704934, + 0, + -0.709273, + -0.704934, + 0, + -0.805117, + -0.593116, + 0, + -0.805117, + -0.593116, + 0, + -0.836303, + -0.548268, + 0, + -0.836303, + -0.548268, + 0, + -0.875386, + -0.483425, + 0, + -0.875386, + -0.483425, + 0, + -0.875386, + -0.483425, + 0, + -0.875386, + -0.483425, + 0, + -0.918489, + -0.395448, + 0, + -0.918489, + -0.395448, + 0, + -0.96096, + -0.276686, + 0, + -0.96096, + -0.276686, + 0, + -0.992928, + -0.118717, + 0, + -0.992928, + -0.118717, + 0, + -0.996681, + 0.081407, + 0, + -0.996681, + 0.081407, + 0, + -0.950716, + 0.310062, + 0, + -0.950716, + 0.310062, + 0, + -0.846403, + 0.532543, + 0, + -0.846403, + 0.532543, + 0, + -0.739773, + 0.672856, + 0, + -0.739773, + 0.672856, + 0, + -0.705695, + 0.708515, + 0, + -0.705695, + 0.708515, + 0, + -0.704984, + 0.709224, + 0, + -0.704984, + 0.709224, + 0, + -0.704567, + 0.709638, + 0, + -0.704567, + 0.709638, + 0, + -0.704293, + 0.709909, + 0, + -0.704293, + 0.709909, + 0, + -0.7041, + 0.710101, + 0, + -0.7041, + 0.710101, + 0, + -0.703956, + 0.710244, + 0, + -0.703956, + 0.710244, + 0, + -0.703846, + 0.710352, + 0, + -0.703846, + 0.710352, + 0, + -0.703761, + 0.710437, + 0, + -0.703761, + 0.710437, + 0, + -0.703697, + 0.7105, + 0, + -0.703697, + 0.7105, + 0, + -0.710296, + 0.703903, + 0, + -0.710296, + 0.703903, + 0, + -0.743801, + 0.668401, + 0, + -0.743801, + 0.668401, + 0, + -0.793106, + 0.609084, + 0, + -0.793106, + 0.609084, + 0, + -0.837953, + 0.545743, + 0, + -0.837953, + 0.545743, + 0, + -0.878022, + 0.478621, + 0, + -0.878022, + 0.478621, + 0, + -0.912985, + 0.407992, + 0, + -0.912985, + 0.407992, + 0, + -0.94251, + 0.334177, + 0, + -0.94251, + 0.334177, + 0, + -0.966261, + 0.257564, + 0, + -0.966261, + 0.257564, + 0, + -0.983919, + 0.178614, + 0, + -0.983919, + 0.178614, + 0, + -0.992634, + 0.121152, + 0, + -0.992634, + 0.121152, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709436, + 0, + -0.704769, + -0.709436, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709437, + 0, + -0.704769, + -0.709436, + 0, + -0.704769, + -0.709437, + 0, + -0.70477, + -0.709436, + 0, + -0.70477, + -0.709436, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.70477, + -0.709436, + 0, + 0.70477, + -0.709436, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.704769, + -0.709437, + 0, + 0.70477, + -0.709436, + 0, + 0.70477, + -0.709436, + 0, + 0.999236, + -0.039076, + 0, + 0.999936, + -0.011337, + 0, + 1, + 0.000539, + 0, + 0.998932, + 0.046208, + 0, + 0.999489, + 0.031972, + 0, + 0.999997, + -0.002432, + 0, + 0.999999, + 0.001508, + 0, + 1, + -0.000097, + 0, + 1, + 0.000138, + 0, + 0.999999, + 0.001662, + 0, + 1, + 0.000104, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0.000001, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0.000002, + 0, + 1, + -0.000006, + 0, + 1, + -0.000002, + 0, + 1, + 0.000003, + 0, + 1, + -0.000002, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0, + 0, + 1, + -0.000002, + 0, + 1, + 0.000002, + 0, + 1, + -0.000002, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0.000002, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0.000004, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + -0.000003, + 0, + 1, + -0.000002, + 0, + 1, + 0.000004, + 0, + 1, + 0.000008, + 0, + 1, + 0.000003, + 0, + 1, + -0.00001, + 0, + 1, + -0.000006, + 0, + 1, + -0.000004, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0.000005, + 0, + 1, + 0.000001, + 0, + 1, + -0.000009, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + 0.000001, + 0, + 1, + 0.000006, + 0, + 1, + 0.000009, + 0, + 1, + 0.000002, + 0, + 1, + -0.000001, + 0, + 1, + -0.000009, + 0, + 1, + -0.000004, + 0, + 1, + 0.000005, + 0, + 1, + 0.000003, + 0, + 1, + -0.000001, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0.000003, + 0, + 1, + -0.000002, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000001, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000001, + 0, + 1, + -0.000003, + 0, + 1, + 0.000008, + 0, + 1, + 0.000006, + 0, + 1, + 0.000001, + 0, + 1, + 0.000001, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0.000001, + 0, + 1, + 0.000002, + 0, + 1, + -0.000003, + 0, + 1, + 0.000003, + 0, + 1, + 0.000002, + 0, + 1, + 0.000001, + 0, + 1, + -0.000001, + 0, + 1, + -0.000002, + 0, + 1, + 0, + 0, + 1, + -0.000001, + 0, + 1, + 0.000001, + 0, + 1, + -0.000005, + 0, + 1, + -0.000006, + 0, + 1, + 0.000006, + 0, + 1, + 0.000002, + 0, + 1, + -0.000009, + 0, + 1, + 0.000004, + 0, + 1, + -0.000004, + 0, + 1, + 0, + 0, + 1, + 0.000003, + 0, + 1, + 0.000002, + 0, + 1, + 0.000008, + 0, + 1, + 0.000015, + 0, + 1, + -0.000003, + 0, + 1, + -0.000003, + 0, + 1, + 0.000006, + 0, + 1, + -0.000013, + 0, + 1, + 0.000025, + 0, + 1, + 0.000013, + 0, + 1, + -0.000023, + 0 + ], + "Tangents": [ + 0.01749, + -0.999847, + 0, + 0.0154, + -0.999881, + 0, + 0.001662, + -0.999999, + 0, + 0.008262, + -0.999966, + 0, + 0.001307, + -0.999999, + 0, + -0.000362, + -1, + -0, + 0.000287, + -1, + 0, + -0.00005, + -1, + -0, + 0.000045, + -1, + 0, + 0.000073, + -1, + 0, + -0.000117, + -1, + -0, + 0.000002, + -1, + 0, + -0.000003, + -1, + -0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + 0.000002, + -1, + 0, + 0.000001, + -1, + 0, + 0.000004, + -1, + 0, + 0.000015, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + -0.000003, + -1, + -0, + -0.000003, + -1, + -0, + -0, + -1, + -0, + 0.000003, + -1, + 0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + -0, + -1, + -0, + -0.000002, + -1, + -0, + 0.000001, + -1, + 0, + -0.000002, + -1, + -0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + -0.000003, + -1, + -0, + -0.000001, + -1, + -0, + 0.000003, + -1, + 0, + -0, + -1, + -0, + -0.000002, + -1, + -0, + -0.000004, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0.000006, + -1, + -0, + -0.00001, + -1, + -0, + 0.000002, + -1, + 0, + -0.000002, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0.000003, + -1, + -0, + -0, + -1, + -0, + 0.000005, + -1, + 0, + 0.000003, + -1, + 0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + -0.000001, + -1, + -0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + -0, + -1, + -0, + -0.000013, + -1, + -0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + -0.000005, + -1, + -0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + 0.000002, + -1, + 0, + -0.000005, + -1, + -0, + -0.000001, + -1, + -0, + -0.000003, + -1, + -0, + 0.000014, + -1, + 0, + 0.00002, + -1, + 0, + -0.00002, + -1, + -0, + -0.000003, + -1, + -0, + -0.000085, + -1, + -0, + -0.000298, + -1, + -0, + 0, + -0, + -1, + 0, + -0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + -0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + 0, + -0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0.11268, + -0.993631, + 0, + 0.093574, + -0.995612, + 0, + 0.0104, + -0.999946, + 0, + 0.037593, + -0.999293, + 0, + 0.000432, + -1, + 0, + 0.004911, + -0.999988, + 0, + 0.013553, + -0.999908, + 0, + 0.00111, + -0.999999, + 0, + 0.001853, + -0.999998, + 0, + -0.003602, + -0.999994, + -0, + 0.001856, + -0.999998, + 0, + -0.000001, + -1, + -0, + -0.000002, + -1, + -0, + -0.000004, + -1, + -0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + 0, + -1, + 0, + -0.000003, + -1, + -0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0.000002, + -1, + -0, + -0.000003, + -1, + -0, + 0.000007, + -1, + 0, + 0, + -1, + 0, + 0.000012, + -1, + 0, + -0.000015, + -1, + -0, + 0.000003, + -1, + 0, + -0.000004, + -1, + -0, + 0.000001, + -1, + 0, + 0.000014, + -1, + 0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0.000002, + -1, + -0, + -0.000002, + -1, + -0, + 0.000004, + -1, + 0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + -0.000007, + -1, + -0, + -0.00001, + -1, + -0, + 0.000006, + -1, + 0, + -0, + -1, + -0, + 0.000003, + -1, + 0, + 0.000006, + -1, + 0, + -0.000016, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0.000002, + -1, + 0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + 0.000002, + -1, + 0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + -0.000001, + -1, + -0, + 0.000002, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0.000004, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + -0, + -1, + -0, + 0, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + -0, + -1, + -0, + -0.000002, + -1, + -0, + 0, + -1, + 0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0.000003, + -1, + -0, + -0.000001, + -1, + -0, + -0.00001, + -1, + -0, + -0.000033, + -1, + -0, + -0.000002, + -1, + -0, + -0.000001, + -1, + -0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + -0.000001, + -1, + -0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + -0, + -1, + -0, + 0, + -1, + 0, + 0.000001, + -1, + 0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + -0, + -1, + -0, + 0.000001, + -1, + 0, + -0.000001, + -1, + -0, + 0, + -1, + 0, + 0.000002, + -1, + 0, + 0.000002, + -1, + 0, + -0.000001, + -1, + -0, + -0.000002, + -1, + -0, + -0.000001, + -1, + -0, + -0.00001, + -1, + -0, + 0.000003, + -1, + 0, + -0.000006, + -1, + -0, + -0.000023, + -1, + -0 + ], + "Normals": [ + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 1, + -0, + 0.121153, + 0.992634, + -0, + 0, + 1, + -0, + 0.121153, + 0.992634, + -0, + 0.178614, + 0.983919, + 0, + 0.178614, + 0.983919, + 0, + 0.25757, + 0.96626, + 0, + 0.25757, + 0.96626, + 0, + 0.334182, + 0.942508, + 0, + 0.334182, + 0.942508, + 0, + 0.40799, + 0.912986, + 0, + 0.40799, + 0.912986, + 0, + 0.478621, + 0.878021, + 0, + 0.478621, + 0.878021, + 0, + 0.545743, + 0.837953, + 0, + 0.545743, + 0.837953, + 0, + 0.609083, + 0.793106, + 0, + 0.609083, + 0.793106, + 0, + 0.668402, + 0.743801, + 0, + 0.668402, + 0.743801, + 0, + 0.703548, + 0.710648, + 0, + 0.703548, + 0.710648, + 0, + 0.709878, + 0.704325, + 0, + 0.709878, + 0.704325, + 0, + 0.709867, + 0.704336, + 0, + 0.709867, + 0.704336, + 0, + 0.709853, + 0.70435, + 0, + 0.709853, + 0.70435, + 0, + 0.709835, + 0.704368, + 0, + 0.709835, + 0.704368, + 0, + 0.709811, + 0.704392, + 0, + 0.709811, + 0.704392, + 0, + 0.70978, + 0.704424, + 0, + 0.70978, + 0.704424, + 0, + 0.709735, + 0.704469, + 0, + 0.709735, + 0.704469, + 0, + 0.709667, + 0.704538, + 0, + 0.709667, + 0.704538, + 0, + 0.70955, + 0.704655, + 0, + 0.70955, + 0.704655, + 0, + 0.674714, + 0.738079, + 0, + 0.674714, + 0.738079, + 0, + 0.534936, + 0.844893, + 0, + 0.534936, + 0.844893, + 0, + 0.311823, + 0.95014, + 0, + 0.311823, + 0.95014, + 0, + 0.081916, + 0.996639, + 0, + 0.081916, + 0.996639, + 0, + -0.119452, + 0.99284, + 0, + -0.119452, + 0.99284, + 0, + -0.278294, + 0.960496, + 0, + -0.278294, + 0.960496, + 0, + -0.397542, + 0.917584, + 0, + -0.397542, + 0.917584, + 0, + -0.485751, + 0.874097, + 0, + -0.485751, + 0.874097, + 0, + -0.485751, + 0.874097, + 0, + -0.550675, + 0.83472, + 0, + -0.485751, + 0.874097, + 0, + -0.550675, + 0.83472, + 0, + -0.59552, + 0.80334, + 0, + -0.59552, + 0.80334, + 0, + -0.705042, + 0.709166, + 0, + -0.705042, + 0.709166, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709279, + 0.704928, + 0, + -0.739063, + 0.673636, + 0, + -0.709279, + 0.704928, + 0, + -0.739063, + 0.673636, + 0, + -0.795818, + 0.605536, + 0, + -0.795818, + 0.605536, + 0, + -0.845547, + 0.5339, + 0, + -0.845547, + 0.5339, + 0, + -0.888276, + 0.45931, + 0, + -0.888276, + 0.45931, + 0, + -0.924078, + 0.382203, + 0, + -0.924078, + 0.382203, + 0, + -0.953016, + 0.302919, + 0, + -0.953016, + 0.302919, + 0, + -0.97511, + 0.221722, + 0, + -0.97511, + 0.221722, + 0, + -0.990317, + 0.138822, + 0, + -0.990317, + 0.138822, + 0, + -0.998516, + 0.054453, + 0, + -0.998516, + 0.054453, + 0, + -1, + 0, + 0, + -1, + 0, + 0, + -0.998516, + -0.054453, + 0, + -0.998516, + -0.054453, + 0, + -0.990317, + -0.138822, + 0, + -0.990317, + -0.138822, + 0, + -0.97511, + -0.221722, + 0, + -0.97511, + -0.221722, + 0, + -0.953016, + -0.302919, + 0, + -0.953016, + -0.302919, + 0, + -0.924078, + -0.382203, + 0, + -0.924078, + -0.382203, + 0, + -0.888276, + -0.45931, + 0, + -0.888276, + -0.45931, + 0, + -0.845547, + -0.5339, + 0, + -0.845547, + -0.5339, + 0, + -0.795818, + -0.605536, + 0, + -0.795818, + -0.605536, + 0, + -0.739063, + -0.673636, + 0, + -0.739063, + -0.673636, + 0, + -0.70928, + -0.704927, + 0, + -0.70928, + -0.704927, + 0, + -0.709436, + -0.70477, + 0, + -0.709436, + -0.70477, + 0, + -0.704934, + -0.709273, + 0, + -0.593116, + -0.805117, + 0, + -0.704934, + -0.709273, + 0, + -0.593116, + -0.805117, + 0, + -0.548268, + -0.836303, + 0, + -0.548268, + -0.836303, + 0, + -0.483425, + -0.875386, + 0, + -0.483425, + -0.875386, + 0, + -0.483425, + -0.875386, + 0, + -0.395448, + -0.918489, + 0, + -0.483425, + -0.875386, + 0, + -0.395448, + -0.918489, + 0, + -0.276686, + -0.96096, + 0, + -0.276686, + -0.96096, + 0, + -0.118717, + -0.992928, + 0, + -0.118717, + -0.992928, + 0, + 0.081407, + -0.996681, + 0, + 0.081407, + -0.996681, + 0, + 0.310062, + -0.950716, + 0, + 0.310062, + -0.950716, + 0, + 0.532543, + -0.846403, + 0, + 0.532543, + -0.846403, + 0, + 0.672857, + -0.739773, + 0, + 0.672857, + -0.739773, + 0, + 0.708515, + -0.705695, + 0, + 0.708515, + -0.705695, + 0, + 0.709224, + -0.704984, + 0, + 0.709224, + -0.704984, + 0, + 0.709637, + -0.704567, + 0, + 0.709637, + -0.704567, + 0, + 0.709909, + -0.704293, + 0, + 0.709909, + -0.704293, + 0, + 0.710101, + -0.7041, + 0, + 0.710101, + -0.7041, + 0, + 0.710244, + -0.703956, + 0, + 0.710244, + -0.703956, + 0, + 0.710352, + -0.703846, + 0, + 0.710352, + -0.703846, + 0, + 0.710437, + -0.703761, + 0, + 0.710437, + -0.703761, + 0, + 0.7105, + -0.703697, + 0, + 0.7105, + -0.703697, + 0, + 0.703903, + -0.710296, + 0, + 0.703903, + -0.710296, + 0, + 0.668401, + -0.743801, + 0, + 0.668401, + -0.743801, + 0, + 0.609084, + -0.793106, + 0, + 0.609084, + -0.793106, + 0, + 0.545743, + -0.837953, + 0, + 0.545743, + -0.837953, + 0, + 0.478621, + -0.878022, + 0, + 0.478621, + -0.878022, + 0, + 0.407992, + -0.912985, + 0, + 0.407992, + -0.912985, + 0, + 0.334177, + -0.94251, + 0, + 0.334177, + -0.94251, + 0, + 0.257564, + -0.966261, + 0, + 0.257564, + -0.966261, + 0, + 0.178614, + -0.983919, + 0, + 0.178614, + -0.983919, + 0, + 0.121152, + -0.992634, + 0, + 0.121152, + -0.992634, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709436, + -0.704769, + 0, + -0.709436, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709437, + -0.704769, + 0, + -0.709436, + -0.704769, + 0, + -0.709436, + -0.704769, + 0, + -0.709436, + -0.70477, + 0, + -0.709436, + -0.70477, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709436, + 0.70477, + 0, + -0.709436, + 0.70477, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709437, + 0.704769, + 0, + -0.709436, + 0.704769, + 0, + -0.709436, + 0.704769, + 0, + -0.709436, + 0.70477, + 0, + -0.709436, + 0.70477, + 0, + 0, + 1, + -0, + 0, + 1, + -0, + -0.121153, + 0.992634, + -0, + -0.121153, + 0.992634, + -0, + -0.178614, + 0.983919, + 0, + -0.178614, + 0.983919, + 0, + -0.25757, + 0.96626, + 0, + -0.25757, + 0.96626, + 0, + -0.334182, + 0.942508, + 0, + -0.334182, + 0.942508, + 0, + -0.40799, + 0.912986, + 0, + -0.40799, + 0.912986, + 0, + -0.478621, + 0.878021, + 0, + -0.478621, + 0.878021, + 0, + -0.545743, + 0.837953, + 0, + -0.545743, + 0.837953, + 0, + -0.609083, + 0.793106, + 0, + -0.609083, + 0.793106, + 0, + -0.668402, + 0.743801, + 0, + -0.668402, + 0.743801, + 0, + -0.703548, + 0.710648, + 0, + -0.703548, + 0.710648, + 0, + -0.709878, + 0.704325, + 0, + -0.709878, + 0.704325, + 0, + -0.709867, + 0.704336, + 0, + -0.709867, + 0.704336, + 0, + -0.709853, + 0.70435, + 0, + -0.709853, + 0.70435, + 0, + -0.709835, + 0.704368, + 0, + -0.709835, + 0.704368, + 0, + -0.709811, + 0.704392, + 0, + -0.709811, + 0.704392, + 0, + -0.70978, + 0.704424, + 0, + -0.70978, + 0.704424, + 0, + -0.709735, + 0.704469, + 0, + -0.709735, + 0.704469, + 0, + -0.709667, + 0.704538, + 0, + -0.709667, + 0.704538, + 0, + -0.70955, + 0.704655, + 0, + -0.70955, + 0.704655, + 0, + -0.674714, + 0.738079, + 0, + -0.674714, + 0.738079, + 0, + -0.534936, + 0.844893, + 0, + -0.534936, + 0.844893, + 0, + -0.311823, + 0.95014, + 0, + -0.311823, + 0.95014, + 0, + -0.081916, + 0.996639, + 0, + -0.081916, + 0.996639, + 0, + 0.119452, + 0.99284, + 0, + 0.119452, + 0.99284, + 0, + 0.278294, + 0.960496, + 0, + 0.278294, + 0.960496, + 0, + 0.397542, + 0.917584, + 0, + 0.397542, + 0.917584, + 0, + 0.485751, + 0.874097, + 0, + 0.485751, + 0.874097, + 0, + 0.485751, + 0.874097, + 0, + 0.485751, + 0.874097, + 0, + 0.550675, + 0.83472, + 0, + 0.550675, + 0.83472, + 0, + 0.59552, + 0.80334, + 0, + 0.59552, + 0.80334, + 0, + 0.705042, + 0.709166, + 0, + 0.705042, + 0.709166, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709279, + 0.704928, + 0, + 0.709279, + 0.704928, + 0, + 0.739063, + 0.673636, + 0, + 0.739063, + 0.673636, + 0, + 0.795818, + 0.605536, + 0, + 0.795818, + 0.605536, + 0, + 0.845547, + 0.5339, + 0, + 0.845547, + 0.5339, + 0, + 0.888276, + 0.45931, + 0, + 0.888276, + 0.45931, + 0, + 0.924078, + 0.382203, + 0, + 0.924078, + 0.382203, + 0, + 0.953016, + 0.302919, + 0, + 0.953016, + 0.302919, + 0, + 0.97511, + 0.221722, + 0, + 0.97511, + 0.221722, + 0, + 0.990317, + 0.138822, + 0, + 0.990317, + 0.138822, + 0, + 0.998516, + 0.054453, + 0, + 0.998516, + 0.054453, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0.998516, + -0.054453, + 0, + 0.998516, + -0.054453, + 0, + 0.990317, + -0.138822, + 0, + 0.990317, + -0.138822, + 0, + 0.97511, + -0.221722, + 0, + 0.97511, + -0.221722, + 0, + 0.953016, + -0.302919, + 0, + 0.953016, + -0.302919, + 0, + 0.924078, + -0.382203, + 0, + 0.924078, + -0.382203, + 0, + 0.888276, + -0.45931, + 0, + 0.888276, + -0.45931, + 0, + 0.845547, + -0.5339, + 0, + 0.845547, + -0.5339, + 0, + 0.795818, + -0.605536, + 0, + 0.795818, + -0.605536, + 0, + 0.739063, + -0.673636, + 0, + 0.739063, + -0.673636, + 0, + 0.70928, + -0.704927, + 0, + 0.70928, + -0.704927, + 0, + 0.709436, + -0.70477, + 0, + 0.709436, + -0.70477, + 0, + 0.704934, + -0.709273, + 0, + 0.704934, + -0.709273, + 0, + 0.593116, + -0.805117, + 0, + 0.593116, + -0.805117, + 0, + 0.548268, + -0.836303, + 0, + 0.548268, + -0.836303, + 0, + 0.483425, + -0.875386, + 0, + 0.483425, + -0.875386, + 0, + 0.483425, + -0.875386, + 0, + 0.483425, + -0.875386, + 0, + 0.395448, + -0.918489, + 0, + 0.395448, + -0.918489, + 0, + 0.276686, + -0.96096, + 0, + 0.276686, + -0.96096, + 0, + 0.118717, + -0.992928, + 0, + 0.118717, + -0.992928, + 0, + -0.081407, + -0.996681, + 0, + -0.081407, + -0.996681, + 0, + -0.310062, + -0.950716, + 0, + -0.310062, + -0.950716, + 0, + -0.532543, + -0.846403, + 0, + -0.532543, + -0.846403, + 0, + -0.672857, + -0.739773, + 0, + -0.672857, + -0.739773, + 0, + -0.708515, + -0.705695, + 0, + -0.708515, + -0.705695, + 0, + -0.709224, + -0.704984, + 0, + -0.709224, + -0.704984, + 0, + -0.709637, + -0.704567, + 0, + -0.709637, + -0.704567, + 0, + -0.709909, + -0.704293, + 0, + -0.709909, + -0.704293, + 0, + -0.710101, + -0.7041, + 0, + -0.710101, + -0.7041, + 0, + -0.710244, + -0.703956, + 0, + -0.710244, + -0.703956, + 0, + -0.710352, + -0.703846, + 0, + -0.710352, + -0.703846, + 0, + -0.710437, + -0.703761, + 0, + -0.710437, + -0.703761, + 0, + -0.7105, + -0.703697, + 0, + -0.7105, + -0.703697, + 0, + -0.703903, + -0.710296, + 0, + -0.703903, + -0.710296, + 0, + -0.668401, + -0.743801, + 0, + -0.668401, + -0.743801, + 0, + -0.609084, + -0.793106, + 0, + -0.609084, + -0.793106, + 0, + -0.545743, + -0.837953, + 0, + -0.545743, + -0.837953, + 0, + -0.478621, + -0.878022, + 0, + -0.478621, + -0.878022, + 0, + -0.407992, + -0.912985, + 0, + -0.407992, + -0.912985, + 0, + -0.334177, + -0.94251, + 0, + -0.334177, + -0.94251, + 0, + -0.257564, + -0.966261, + 0, + -0.257564, + -0.966261, + 0, + -0.178614, + -0.983919, + 0, + -0.178614, + -0.983919, + 0, + -0.121152, + -0.992634, + 0, + -0.121152, + -0.992634, + 0, + 0, + -1, + 0, + 0, + -1, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709436, + -0.704769, + 0, + 0.709436, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709437, + -0.704769, + 0, + 0.709436, + -0.704769, + 0, + 0.709436, + -0.704769, + 0, + 0.709436, + -0.70477, + 0, + 0.709436, + -0.70477, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709436, + 0.70477, + 0, + 0.709436, + 0.70477, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709437, + 0.704769, + 0, + 0.709436, + 0.704769, + 0, + 0.709436, + 0.704769, + 0, + 0.709436, + 0.70477, + 0, + 0.709436, + 0.70477, + 0, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1, + -0, + 0, + 1 + ], + "Texture Coordinates": [ + 0.182938, + 0.458134, + 0.17296, + 0.44825, + 0.186552, + 0.461712, + 0.157909, + 0.43334, + 0.13908, + 0.41468, + 0.117763, + 0.393549, + 0.095253, + 0.371224, + 0.072842, + 0.348983, + 0.051823, + 0.328104, + 0.033488, + 0.309865, + 0.019131, + 0.295543, + 0.009462, + 0.28345, + 0.003852, + 0.271258, + 0.001584, + 0.259292, + 0.001942, + 0.247875, + 0.00421, + 0.237328, + 0.007672, + 0.227974, + 0.011611, + 0.220138, + 0.015312, + 0.21414, + 0.018057, + 0.210305, + 0.019131, + 0.208955, + 0.040264, + 0.187961, + 0.061397, + 0.166968, + 0.082529, + 0.145974, + 0.103662, + 0.124981, + 0.124795, + 0.103987, + 0.145927, + 0.082993, + 0.16706, + 0.062, + 0.188192, + 0.041006, + 0.189718, + 0.464975, + 0.209325, + 0.020013, + 0.192604, + 0.468447, + 0.212774, + 0.016569, + 0.216494, + 0.013454, + 0.220458, + 0.010681, + 0.224637, + 0.008265, + 0.229001, + 0.006217, + 0.233524, + 0.004553, + 0.238175, + 0.003285, + 0.242927, + 0.002427, + 0.247751, + 0.001992, + 0.252619, + 0.001994, + 0.257487, + 0.002053, + 0.262311, + 0.002523, + 0.267063, + 0.003395, + 0.271715, + 0.004661, + 0.276237, + 0.006311, + 0.280602, + 0.008337, + 0.28478, + 0.010729, + 0.288744, + 0.013478, + 0.292464, + 0.016576, + 0.295913, + 0.020013, + 0.317046, + 0.041006, + 0.338178, + 0.062, + 0.359311, + 0.082993, + 0.380444, + 0.103987, + 0.401576, + 0.124981, + 0.422709, + 0.145974, + 0.443842, + 0.166968, + 0.464974, + 0.187961, + 0.486107, + 0.208955, + 0.487181, + 0.210305, + 0.489927, + 0.21414, + 0.493627, + 0.220138, + 0.497566, + 0.227974, + 0.501028, + 0.237328, + 0.503296, + 0.247875, + 0.503654, + 0.259292, + 0.501386, + 0.271258, + 0.495776, + 0.28345, + 0.486107, + 0.295543, + 0.47175, + 0.309865, + 0.453415, + 0.328104, + 0.432396, + 0.348983, + 0.409985, + 0.371224, + 0.387475, + 0.393549, + 0.366159, + 0.41468, + 0.347329, + 0.43334, + 0.332278, + 0.44825, + 0.3223, + 0.458134, + 0.318686, + 0.461712, + 0.31552, + 0.464975, + 0.312634, + 0.468447, + 0.195198, + 0.472111, + 0.31004, + 0.472111, + 0.197491, + 0.475951, + 0.307747, + 0.475951, + 0.199471, + 0.47995, + 0.305767, + 0.47995, + 0.201129, + 0.484091, + 0.304109, + 0.484091, + 0.202453, + 0.488359, + 0.302785, + 0.488359, + 0.203433, + 0.492736, + 0.301805, + 0.492736, + 0.204059, + 0.497206, + 0.301179, + 0.497206, + 0.20432, + 0.500001, + 0.300918, + 0.500001, + 0.204059, + 0.502796, + 0.301179, + 0.502796, + 0.301805, + 0.507267, + 0.203433, + 0.507267, + 0.302785, + 0.511644, + 0.202453, + 0.511644, + 0.304109, + 0.515911, + 0.201129, + 0.515911, + 0.305767, + 0.520053, + 0.199471, + 0.520053, + 0.307747, + 0.524052, + 0.197491, + 0.524052, + 0.31004, + 0.527891, + 0.195198, + 0.527891, + 0.312634, + 0.531555, + 0.192604, + 0.531555, + 0.31552, + 0.535027, + 0.189718, + 0.535027, + 0.318686, + 0.53829, + 0.186552, + 0.53829, + 0.3223, + 0.541869, + 0.182945, + 0.541869, + 0.332278, + 0.551752, + 0.172984, + 0.551752, + 0.347329, + 0.566662, + 0.157957, + 0.566662, + 0.366159, + 0.585322, + 0.139152, + 0.585322, + 0.387475, + 0.606453, + 0.117857, + 0.606453, + 0.409985, + 0.628778, + 0.095361, + 0.628778, + 0.432396, + 0.651019, + 0.072952, + 0.651019, + 0.453415, + 0.671898, + 0.051919, + 0.671898, + 0.47175, + 0.690137, + 0.033549, + 0.690137, + 0.486107, + 0.704459, + 0.495776, + 0.716553, + 0.501386, + 0.728744, + 0.503654, + 0.74071, + 0.503296, + 0.752128, + 0.501028, + 0.762675, + 0.497566, + 0.772028, + 0.493627, + 0.779865, + 0.489927, + 0.785862, + 0.487181, + 0.789697, + 0.486107, + 0.791047, + 0.464974, + 0.812041, + 0.443842, + 0.833035, + 0.422709, + 0.854028, + 0.401576, + 0.875022, + 0.380444, + 0.896015, + 0.359311, + 0.917009, + 0.338178, + 0.938002, + 0.317046, + 0.958996, + 0.295913, + 0.97999, + 0.019131, + 0.704459, + 0.009402, + 0.716553, + 0.003756, + 0.728744, + 0.001474, + 0.74071, + 0.001834, + 0.752128, + 0.004116, + 0.762675, + 0.0076, + 0.772028, + 0.011564, + 0.779865, + 0.015288, + 0.785862, + 0.01805, + 0.789697, + 0.019131, + 0.791047, + 0.040264, + 0.812041, + 0.061397, + 0.833035, + 0.082529, + 0.854028, + 0.103662, + 0.875022, + 0.124795, + 0.896015, + 0.145927, + 0.917009, + 0.16706, + 0.938002, + 0.188192, + 0.958996, + 0.209325, + 0.97999, + 0.292464, + 0.983427, + 0.288744, + 0.986524, + 0.28478, + 0.989274, + 0.280602, + 0.991666, + 0.276237, + 0.993691, + 0.271715, + 0.995341, + 0.267063, + 0.996607, + 0.262311, + 0.997479, + 0.257487, + 0.997949, + 0.252619, + 0.998008, + 0.247751, + 0.997949, + 0.242927, + 0.997479, + 0.238175, + 0.996607, + 0.233524, + 0.995341, + 0.229001, + 0.993691, + 0.224637, + 0.991666, + 0.220458, + 0.989274, + 0.216494, + 0.986524, + 0.212774, + 0.983427, + 0.616224, + 0.001992, + 0.616224, + 0.006539, + 0.565973, + 0.001992, + 0.565973, + 0.006539, + 0.616224, + 0.011009, + 0.565973, + 0.011009, + 0.616224, + 0.015386, + 0.565973, + 0.015386, + 0.616224, + 0.019654, + 0.565973, + 0.019654, + 0.616224, + 0.023795, + 0.565973, + 0.023795, + 0.616224, + 0.027794, + 0.565973, + 0.027794, + 0.616224, + 0.031634, + 0.565973, + 0.031634, + 0.616224, + 0.035298, + 0.565973, + 0.035298, + 0.616224, + 0.03877, + 0.565973, + 0.03877, + 0.616224, + 0.042033, + 0.565973, + 0.042033, + 0.616224, + 0.045611, + 0.565973, + 0.045611, + 0.616224, + 0.055494, + 0.565973, + 0.055494, + 0.616224, + 0.070405, + 0.565973, + 0.070405, + 0.616224, + 0.089065, + 0.565973, + 0.089065, + 0.616224, + 0.110196, + 0.565973, + 0.110196, + 0.616224, + 0.132521, + 0.565973, + 0.132521, + 0.616224, + 0.154762, + 0.565973, + 0.154762, + 0.616224, + 0.175641, + 0.565973, + 0.175641, + 0.616224, + 0.19388, + 0.565973, + 0.19388, + 0.616224, + 0.208202, + 0.565973, + 0.208202, + 0.616224, + 0.220295, + 0.565973, + 0.220295, + 0.616224, + 0.232487, + 0.565973, + 0.232487, + 0.616224, + 0.244453, + 0.565973, + 0.244453, + 0.616224, + 0.25587, + 0.565973, + 0.25587, + 0.616224, + 0.266417, + 0.565973, + 0.266417, + 0.616224, + 0.275771, + 0.565973, + 0.275771, + 0.616224, + 0.283607, + 0.565973, + 0.283607, + 0.509723, + 0.484055, + 0.509723, + 0.480332, + 0.559974, + 0.484055, + 0.559974, + 0.480332, + 0.509723, + 0.477569, + 0.559974, + 0.477569, + 0.509723, + 0.476488, + 0.559974, + 0.476488, + 0.509723, + 0.455355, + 0.559974, + 0.455355, + 0.509723, + 0.286294, + 0.509723, + 0.282845, + 0.559974, + 0.286294, + 0.559974, + 0.282845, + 0.509723, + 0.279125, + 0.559974, + 0.279125, + 0.509723, + 0.275161, + 0.559974, + 0.275161, + 0.509723, + 0.270983, + 0.559974, + 0.270983, + 0.509723, + 0.266618, + 0.559974, + 0.266618, + 0.509723, + 0.262095, + 0.559974, + 0.262095, + 0.509723, + 0.257444, + 0.559974, + 0.257444, + 0.509723, + 0.252692, + 0.559974, + 0.252692, + 0.509723, + 0.247868, + 0.559974, + 0.247868, + 0.509723, + 0.243, + 0.559974, + 0.243, + 0.509723, + 0.238132, + 0.559974, + 0.238132, + 0.509723, + 0.233308, + 0.559974, + 0.233308, + 0.509723, + 0.228556, + 0.559974, + 0.228556, + 0.509723, + 0.223905, + 0.559974, + 0.223905, + 0.509723, + 0.219382, + 0.559974, + 0.219382, + 0.509723, + 0.215018, + 0.559974, + 0.215018, + 0.509723, + 0.210839, + 0.559974, + 0.210839, + 0.509723, + 0.206875, + 0.559974, + 0.206875, + 0.509723, + 0.203155, + 0.559974, + 0.203155, + 0.509723, + 0.199706, + 0.559974, + 0.199706, + 0.509723, + 0.178573, + 0.559974, + 0.178573, + 0.509723, + 0.009512, + 0.509723, + 0.008438, + 0.559974, + 0.009512, + 0.559974, + 0.008438, + 0.509723, + 0.005693, + 0.559974, + 0.005693, + 0.509723, + 0.001992, + 0.559974, + 0.001992, + 0.622224, + 0.283607, + 0.622224, + 0.275771, + 0.672475, + 0.283607, + 0.672475, + 0.275771, + 0.622224, + 0.266417, + 0.672475, + 0.266417, + 0.622224, + 0.25587, + 0.672475, + 0.25587, + 0.622224, + 0.244453, + 0.672475, + 0.244453, + 0.622224, + 0.232487, + 0.672475, + 0.232487, + 0.622224, + 0.220295, + 0.672475, + 0.220295, + 0.622224, + 0.208202, + 0.672475, + 0.208202, + 0.622224, + 0.19388, + 0.672475, + 0.19388, + 0.622224, + 0.175641, + 0.672475, + 0.175641, + 0.622224, + 0.154762, + 0.672475, + 0.154762, + 0.622224, + 0.132521, + 0.672475, + 0.132521, + 0.622224, + 0.110196, + 0.672475, + 0.110196, + 0.622224, + 0.089065, + 0.672475, + 0.089065, + 0.622224, + 0.070405, + 0.672475, + 0.070405, + 0.622224, + 0.055494, + 0.672475, + 0.055494, + 0.622224, + 0.045611, + 0.672475, + 0.045611, + 0.622224, + 0.042033, + 0.672475, + 0.042033, + 0.622224, + 0.03877, + 0.672475, + 0.03877, + 0.622224, + 0.035298, + 0.672475, + 0.035298, + 0.622224, + 0.031634, + 0.672475, + 0.031634, + 0.622224, + 0.027794, + 0.672475, + 0.027794, + 0.622224, + 0.023795, + 0.672475, + 0.023795, + 0.622224, + 0.019654, + 0.672475, + 0.019654, + 0.622224, + 0.015386, + 0.672475, + 0.015386, + 0.622224, + 0.011009, + 0.672475, + 0.011009, + 0.622224, + 0.006539, + 0.672475, + 0.006539, + 0.622224, + 0.001992, + 0.672475, + 0.001992, + 0.559974, + 0.030645, + 0.509723, + 0.030645, + 0.559974, + 0.051778, + 0.509723, + 0.051778, + 0.559974, + 0.07291, + 0.509723, + 0.07291, + 0.559974, + 0.094043, + 0.509723, + 0.094043, + 0.559974, + 0.115176, + 0.509723, + 0.115176, + 0.559974, + 0.136308, + 0.509723, + 0.136308, + 0.559974, + 0.157441, + 0.509723, + 0.157441, + 0.559974, + 0.307427, + 0.509723, + 0.307427, + 0.559974, + 0.328559, + 0.509723, + 0.328559, + 0.559974, + 0.349692, + 0.509723, + 0.349692, + 0.559974, + 0.370825, + 0.509723, + 0.370825, + 0.559974, + 0.391957, + 0.509723, + 0.391957, + 0.559974, + 0.41309, + 0.509723, + 0.41309, + 0.559974, + 0.434222, + 0.509723, + 0.434222, + 0.616224, + 0.001992, + 0.565973, + 0.001992, + 0.616224, + 0.006539, + 0.565973, + 0.006539, + 0.616224, + 0.011009, + 0.565973, + 0.011009, + 0.616224, + 0.015386, + 0.565973, + 0.015386, + 0.616224, + 0.019654, + 0.565973, + 0.019654, + 0.616224, + 0.023795, + 0.565973, + 0.023795, + 0.616224, + 0.027794, + 0.565973, + 0.027794, + 0.616224, + 0.031634, + 0.565973, + 0.031634, + 0.616224, + 0.035298, + 0.565973, + 0.035298, + 0.616224, + 0.03877, + 0.565973, + 0.03877, + 0.616224, + 0.042033, + 0.565973, + 0.042033, + 0.616224, + 0.045611, + 0.565973, + 0.045611, + 0.616224, + 0.055494, + 0.565973, + 0.055494, + 0.616224, + 0.070405, + 0.565973, + 0.070405, + 0.616224, + 0.089065, + 0.565973, + 0.089065, + 0.616224, + 0.110196, + 0.565973, + 0.110196, + 0.616224, + 0.132521, + 0.565973, + 0.132521, + 0.616224, + 0.154762, + 0.565973, + 0.154762, + 0.616224, + 0.175641, + 0.565973, + 0.175641, + 0.616224, + 0.19388, + 0.565973, + 0.19388, + 0.616224, + 0.208202, + 0.565973, + 0.208202, + 0.616224, + 0.220295, + 0.565973, + 0.220295, + 0.616224, + 0.232487, + 0.565973, + 0.232487, + 0.616224, + 0.244453, + 0.565973, + 0.244453, + 0.616224, + 0.25587, + 0.565973, + 0.25587, + 0.616224, + 0.266417, + 0.565973, + 0.266417, + 0.616224, + 0.275771, + 0.565973, + 0.275771, + 0.616224, + 0.283607, + 0.565973, + 0.283607, + 0.509723, + 0.484055, + 0.559974, + 0.484055, + 0.509723, + 0.480332, + 0.559974, + 0.480332, + 0.509723, + 0.477569, + 0.559974, + 0.477569, + 0.509723, + 0.476488, + 0.559974, + 0.476488, + 0.509723, + 0.455355, + 0.559974, + 0.455355, + 0.509723, + 0.286294, + 0.559974, + 0.286294, + 0.509723, + 0.282845, + 0.559974, + 0.282845, + 0.509723, + 0.279125, + 0.559974, + 0.279125, + 0.509723, + 0.275161, + 0.559974, + 0.275161, + 0.509723, + 0.270983, + 0.559974, + 0.270983, + 0.509723, + 0.266618, + 0.559974, + 0.266618, + 0.509723, + 0.262095, + 0.559974, + 0.262095, + 0.509723, + 0.257444, + 0.559974, + 0.257444, + 0.509723, + 0.252692, + 0.559974, + 0.252692, + 0.509723, + 0.247868, + 0.559974, + 0.247868, + 0.509723, + 0.243, + 0.559974, + 0.243, + 0.509723, + 0.238132, + 0.559974, + 0.238132, + 0.509723, + 0.233308, + 0.559974, + 0.233308, + 0.509723, + 0.228556, + 0.559974, + 0.228556, + 0.509723, + 0.223905, + 0.559974, + 0.223905, + 0.509723, + 0.219382, + 0.559974, + 0.219382, + 0.509723, + 0.215018, + 0.559974, + 0.215018, + 0.509723, + 0.210839, + 0.559974, + 0.210839, + 0.509723, + 0.206875, + 0.559974, + 0.206875, + 0.509723, + 0.203155, + 0.559974, + 0.203155, + 0.509723, + 0.199706, + 0.559974, + 0.199706, + 0.509723, + 0.178573, + 0.559974, + 0.178573, + 0.509723, + 0.009512, + 0.559974, + 0.009512, + 0.509723, + 0.008438, + 0.559974, + 0.008438, + 0.509723, + 0.005693, + 0.559974, + 0.005693, + 0.509723, + 0.001992, + 0.559974, + 0.001992, + 0.622224, + 0.283607, + 0.672475, + 0.283607, + 0.622224, + 0.275771, + 0.672475, + 0.275771, + 0.622224, + 0.266417, + 0.672475, + 0.266417, + 0.622224, + 0.25587, + 0.672475, + 0.25587, + 0.622224, + 0.244453, + 0.672475, + 0.244453, + 0.622224, + 0.232487, + 0.672475, + 0.232487, + 0.622224, + 0.220295, + 0.672475, + 0.220295, + 0.622224, + 0.208202, + 0.672475, + 0.208202, + 0.622224, + 0.19388, + 0.672475, + 0.19388, + 0.622224, + 0.175641, + 0.672475, + 0.175641, + 0.622224, + 0.154762, + 0.672475, + 0.154762, + 0.622224, + 0.132521, + 0.672475, + 0.132521, + 0.622224, + 0.110196, + 0.672475, + 0.110196, + 0.622224, + 0.089065, + 0.672475, + 0.089065, + 0.622224, + 0.070405, + 0.672475, + 0.070405, + 0.622224, + 0.055494, + 0.672475, + 0.055494, + 0.622224, + 0.045611, + 0.672475, + 0.045611, + 0.622224, + 0.042033, + 0.672475, + 0.042033, + 0.622224, + 0.03877, + 0.672475, + 0.03877, + 0.622224, + 0.035298, + 0.672475, + 0.035298, + 0.622224, + 0.031634, + 0.672475, + 0.031634, + 0.622224, + 0.027794, + 0.672475, + 0.027794, + 0.622224, + 0.023795, + 0.672475, + 0.023795, + 0.622224, + 0.019654, + 0.672475, + 0.019654, + 0.622224, + 0.015386, + 0.672475, + 0.015386, + 0.622224, + 0.011009, + 0.672475, + 0.011009, + 0.622224, + 0.006539, + 0.672475, + 0.006539, + 0.622224, + 0.001992, + 0.672475, + 0.001992, + 0.559974, + 0.030645, + 0.509723, + 0.030645, + 0.559974, + 0.051778, + 0.509723, + 0.051778, + 0.559974, + 0.07291, + 0.509723, + 0.07291, + 0.559974, + 0.094043, + 0.509723, + 0.094043, + 0.559974, + 0.115176, + 0.509723, + 0.115176, + 0.559974, + 0.136308, + 0.509723, + 0.136308, + 0.559974, + 0.157441, + 0.509723, + 0.157441, + 0.559974, + 0.307427, + 0.509723, + 0.307427, + 0.559974, + 0.328559, + 0.509723, + 0.328559, + 0.559974, + 0.349692, + 0.509723, + 0.349692, + 0.559974, + 0.370825, + 0.509723, + 0.370825, + 0.559974, + 0.391957, + 0.509723, + 0.391957, + 0.559974, + 0.41309, + 0.509723, + 0.41309, + 0.559974, + 0.434222, + 0.509723, + 0.434222, + 0.182945, + 0.541869, + 0.172984, + 0.551752, + 0.186552, + 0.53829, + 0.157957, + 0.566662, + 0.139152, + 0.585322, + 0.117857, + 0.606453, + 0.095361, + 0.628778, + 0.072952, + 0.651019, + 0.051919, + 0.671898, + 0.033549, + 0.690137, + 0.019131, + 0.704459, + 0.009402, + 0.716553, + 0.003756, + 0.728744, + 0.001474, + 0.74071, + 0.001834, + 0.752128, + 0.004116, + 0.762675, + 0.0076, + 0.772028, + 0.011564, + 0.779865, + 0.015288, + 0.785862, + 0.01805, + 0.789697, + 0.019131, + 0.791047, + 0.040264, + 0.812041, + 0.061397, + 0.833035, + 0.082529, + 0.854028, + 0.103662, + 0.875022, + 0.124795, + 0.896015, + 0.145927, + 0.917009, + 0.16706, + 0.938002, + 0.188192, + 0.958996, + 0.189718, + 0.535027, + 0.209325, + 0.97999, + 0.192604, + 0.531555, + 0.212774, + 0.983427, + 0.216494, + 0.986524, + 0.220458, + 0.989274, + 0.224637, + 0.991666, + 0.229001, + 0.993691, + 0.233524, + 0.995341, + 0.238175, + 0.996607, + 0.242927, + 0.997479, + 0.247751, + 0.997949, + 0.252619, + 0.998008, + 0.257487, + 0.997949, + 0.262311, + 0.997479, + 0.267063, + 0.996607, + 0.271715, + 0.995341, + 0.276237, + 0.993691, + 0.280602, + 0.991666, + 0.28478, + 0.989274, + 0.288744, + 0.986524, + 0.292464, + 0.983427, + 0.295913, + 0.97999, + 0.317046, + 0.958996, + 0.338178, + 0.938002, + 0.359311, + 0.917009, + 0.380444, + 0.896015, + 0.401576, + 0.875022, + 0.422709, + 0.854028, + 0.443842, + 0.833035, + 0.464974, + 0.812041, + 0.486107, + 0.791047, + 0.487181, + 0.789697, + 0.489927, + 0.785862, + 0.493627, + 0.779865, + 0.497566, + 0.772028, + 0.501028, + 0.762675, + 0.503296, + 0.752128, + 0.503654, + 0.74071, + 0.501386, + 0.728744, + 0.495776, + 0.716553, + 0.486107, + 0.704459, + 0.47175, + 0.690137, + 0.453415, + 0.671898, + 0.432396, + 0.651019, + 0.409985, + 0.628778, + 0.387475, + 0.606453, + 0.366159, + 0.585322, + 0.347329, + 0.566662, + 0.332278, + 0.551752, + 0.3223, + 0.541869, + 0.318686, + 0.53829, + 0.31552, + 0.535027, + 0.312634, + 0.531555, + 0.195198, + 0.527891, + 0.31004, + 0.527891, + 0.197491, + 0.524052, + 0.307747, + 0.524052, + 0.199471, + 0.520053, + 0.305767, + 0.520053, + 0.201129, + 0.515911, + 0.304109, + 0.515911, + 0.202453, + 0.511644, + 0.302785, + 0.511644, + 0.203433, + 0.507267, + 0.301805, + 0.507267, + 0.204059, + 0.502796, + 0.301179, + 0.502796, + 0.20432, + 0.500001, + 0.300918, + 0.500001, + 0.204059, + 0.497206, + 0.301179, + 0.497206, + 0.301805, + 0.492736, + 0.203433, + 0.492736, + 0.302785, + 0.488359, + 0.202453, + 0.488359, + 0.304109, + 0.484091, + 0.201129, + 0.484091, + 0.305767, + 0.47995, + 0.199471, + 0.47995, + 0.307747, + 0.475951, + 0.197491, + 0.475951, + 0.31004, + 0.472111, + 0.195198, + 0.472111, + 0.312634, + 0.468447, + 0.192604, + 0.468447, + 0.31552, + 0.464975, + 0.189718, + 0.464975, + 0.318686, + 0.461712, + 0.186552, + 0.461712, + 0.3223, + 0.458134, + 0.182938, + 0.458134, + 0.332278, + 0.44825, + 0.17296, + 0.44825, + 0.347329, + 0.43334, + 0.157909, + 0.43334, + 0.366159, + 0.41468, + 0.13908, + 0.41468, + 0.387475, + 0.393549, + 0.117763, + 0.393549, + 0.409985, + 0.371224, + 0.095253, + 0.371224, + 0.432396, + 0.348983, + 0.072842, + 0.348983, + 0.453415, + 0.328104, + 0.051823, + 0.328104, + 0.47175, + 0.309865, + 0.033488, + 0.309865, + 0.486107, + 0.295543, + 0.495776, + 0.28345, + 0.501386, + 0.271258, + 0.503654, + 0.259292, + 0.503296, + 0.247875, + 0.501028, + 0.237328, + 0.497566, + 0.227974, + 0.493627, + 0.220138, + 0.489927, + 0.21414, + 0.487181, + 0.210305, + 0.486107, + 0.208955, + 0.464974, + 0.187961, + 0.443842, + 0.166968, + 0.422709, + 0.145974, + 0.401576, + 0.124981, + 0.380444, + 0.103987, + 0.359311, + 0.082993, + 0.338178, + 0.062, + 0.317046, + 0.041006, + 0.295913, + 0.020013, + 0.019131, + 0.295543, + 0.009462, + 0.28345, + 0.003852, + 0.271258, + 0.001584, + 0.259292, + 0.001942, + 0.247875, + 0.00421, + 0.237328, + 0.007672, + 0.227974, + 0.011611, + 0.220138, + 0.015312, + 0.21414, + 0.018057, + 0.210305, + 0.019131, + 0.208955, + 0.040264, + 0.187961, + 0.061397, + 0.166968, + 0.082529, + 0.145974, + 0.103662, + 0.124981, + 0.124795, + 0.103987, + 0.145927, + 0.082993, + 0.16706, + 0.062, + 0.188192, + 0.041006, + 0.209325, + 0.020013, + 0.292464, + 0.016576, + 0.288744, + 0.013478, + 0.28478, + 0.010729, + 0.280602, + 0.008337, + 0.276237, + 0.006311, + 0.271715, + 0.004661, + 0.267063, + 0.003395, + 0.262311, + 0.002523, + 0.257487, + 0.002053, + 0.252619, + 0.001994, + 0.247751, + 0.001992, + 0.242927, + 0.002427, + 0.238175, + 0.003285, + 0.233524, + 0.004553, + 0.229001, + 0.006217, + 0.224637, + 0.008265, + 0.220458, + 0.010681, + 0.216494, + 0.013454, + 0.212774, + 0.016569 + ], + "Specular Texture Index": 0, + "Positions": [ + 1.01036, + 1.680277, + -0.575, + 1.248874, + 1.921086, + -0.575, + 0.924003, + 1.593074, + -0.575, + 1.608709, + 2.284303, + -0.575, + 2.059026, + 2.738727, + -0.575, + 2.568988, + 3.253158, + -0.575, + 3.107758, + 3.796397, + -0.575, + 3.644499, + 4.337245, + -0.575, + 4.148373, + 4.844501, + -0.575, + 4.588543, + 5.286966, + -0.575, + 4.934174, + 5.633441, + -0.575, + 5.226027, + 5.866786, + -0.575, + 5.520238, + 6.002184, + -0.575, + 5.809012, + 6.056918, + -0.575, + 6.084561, + 6.048276, + -0.575, + 6.339092, + 5.993541, + -0.575, + 6.564814, + 5.909997, + -0.575, + 6.753939, + 5.814932, + -0.575, + 6.898673, + 5.725626, + -0.575, + 6.991227, + 5.659369, + -0.575, + 7.02381, + 5.633441, + -0.575, + 7.53045, + 5.123446, + -0.575, + 8.03709, + 4.613451, + -0.575, + 8.543729, + 4.103457, + -0.575, + 9.050369, + 3.593462, + -0.575, + 9.557008, + 3.083467, + -0.575, + 10.063648, + 2.573472, + -0.575, + 10.570287, + 2.063477, + -0.575, + 11.076928, + 1.553482, + -0.575, + 0.845254, + 1.516657, + -0.575, + 11.583566, + 1.043487, + -0.575, + 0.76147, + 1.447016, + -0.575, + 11.666675, + 0.960258, + -0.575, + 11.741847, + 0.87047, + -0.575, + 11.80876, + 0.774812, + -0.575, + 11.867082, + 0.673972, + -0.575, + 11.916491, + 0.568638, + -0.575, + 11.956657, + 0.4595, + -0.575, + 11.987259, + 0.347245, + -0.575, + 12.007968, + 0.232563, + -0.575, + 12.01846, + 0.116141, + -0.575, + 12.018405, + -0.001332, + -0.575, + 12.016992, + -0.118804, + -0.575, + 12.005651, + -0.235226, + -0.575, + 11.984597, + -0.349908, + -0.575, + 11.954048, + -0.462163, + -0.575, + 11.914224, + -0.571301, + -0.575, + 11.865342, + -0.676635, + -0.575, + 11.807617, + -0.777475, + -0.575, + 11.741269, + -0.873133, + -0.575, + 11.666512, + -0.962921, + -0.575, + 11.583566, + -1.04615, + -0.575, + 11.076928, + -1.556144, + -0.575, + 10.570287, + -2.066139, + -0.575, + 10.063648, + -2.576134, + -0.575, + 9.557008, + -3.086129, + -0.575, + 9.050367, + -3.596124, + -0.575, + 8.543729, + -4.106119, + -0.575, + 8.037088, + -4.616114, + -0.575, + 7.53045, + -5.126109, + -0.575, + 7.02381, + -5.636104, + -0.575, + 6.991227, + -5.662032, + -0.575, + 6.898673, + -5.72829, + -0.575, + 6.753939, + -5.817595, + -0.575, + 6.564814, + -5.912661, + -0.575, + 6.339092, + -5.996204, + -0.575, + 6.084561, + -6.050939, + -0.575, + 5.809012, + -6.059581, + -0.575, + 5.520238, + -6.004847, + -0.575, + 5.226027, + -5.869449, + -0.575, + 4.934174, + -5.636104, + -0.575, + 4.588543, + -5.289629, + -0.575, + 4.148373, + -4.847163, + -0.575, + 3.644499, + -4.339907, + -0.575, + 3.107758, + -3.79906, + -0.575, + 2.568988, + -3.25582, + -0.575, + 2.059026, + -2.741389, + -0.575, + 1.608709, + -2.286965, + -0.575, + 1.248874, + -1.923749, + -0.575, + 1.01036, + -1.682939, + -0.575, + 0.924003, + -1.595736, + -0.575, + 0.845254, + -1.51932, + -0.575, + 0.76147, + -1.449679, + -0.575, + 0.673046, + 1.384406, + -0.575, + 0.673046, + -1.387068, + -0.575, + 0.580384, + 1.329079, + -0.575, + 0.580384, + -1.331742, + -0.575, + 0.48388, + 1.281289, + -0.575, + 0.48388, + -1.283952, + -0.575, + 0.383935, + 1.24129, + -0.575, + 0.383935, + -1.243953, + -0.575, + 0.280944, + 1.209335, + -0.575, + 0.280944, + -1.211998, + -0.575, + 0.175309, + 1.185678, + -0.575, + 0.175309, + -1.188342, + -0.575, + 0.067427, + 1.170574, + -0.575, + 0.067427, + -1.173238, + -0.575, + -0.000028, + 1.164275, + -0.575, + -0.000028, + -1.166939, + -0.575, + -0.067481, + 1.170574, + -0.575, + -0.067481, + -1.173238, + -0.575, + -0.175364, + -1.188342, + -0.575, + -0.175364, + 1.185678, + -0.575, + -0.281, + -1.211998, + -0.575, + -0.281, + 1.209335, + -0.575, + -0.383989, + -1.243953, + -0.575, + -0.383989, + 1.24129, + -0.575, + -0.483936, + -1.283952, + -0.575, + -0.483936, + 1.281289, + -0.575, + -0.580439, + -1.331742, + -0.575, + -0.580439, + 1.329079, + -0.575, + -0.673102, + -1.387068, + -0.575, + -0.673102, + 1.384406, + -0.575, + -0.761525, + -1.449679, + -0.575, + -0.761525, + 1.447016, + -0.575, + -0.84531, + -1.51932, + -0.575, + -0.84531, + 1.516657, + -0.575, + -0.924058, + -1.595736, + -0.575, + -0.924058, + 1.593074, + -0.575, + -1.010416, + -1.682939, + -0.575, + -1.010416, + 1.680114, + -0.575, + -1.24893, + -1.923749, + -0.575, + -1.24893, + 1.920507, + -0.575, + -1.608765, + -2.286965, + -0.575, + -1.608765, + 2.283161, + -0.575, + -2.059081, + -2.741389, + -0.575, + -2.059081, + 2.736987, + -0.575, + -2.569043, + -3.25582, + -0.575, + -2.569043, + 3.250893, + -0.575, + -3.107813, + -3.79906, + -0.575, + -3.107813, + 3.793788, + -0.575, + -3.644554, + -4.339907, + -0.575, + -3.644554, + 4.334581, + -0.575, + -4.148428, + -4.847163, + -0.575, + -4.148428, + 4.842182, + -0.575, + -4.588599, + -5.289629, + -0.575, + -4.588599, + 5.285498, + -0.575, + -4.934228, + -5.636104, + -0.575, + -5.226083, + -5.869449, + -0.575, + -5.520293, + -6.004847, + -0.575, + -5.809068, + -6.059581, + -0.575, + -6.084616, + -6.050939, + -0.575, + -6.339147, + -5.996204, + -0.575, + -6.56487, + -5.912661, + -0.575, + -6.753994, + -5.817595, + -0.575, + -6.898729, + -5.72829, + -0.575, + -6.991283, + -5.662032, + -0.575, + -7.023865, + -5.636104, + -0.575, + -7.530505, + -5.126109, + -0.575, + -8.037145, + -4.616115, + -0.575, + -8.543784, + -4.10612, + -0.575, + -9.050424, + -3.596124, + -0.575, + -9.557063, + -3.086129, + -0.575, + -10.063703, + -2.576134, + -0.575, + -10.570343, + -2.066139, + -0.575, + -11.076983, + -1.556144, + -0.575, + -11.583623, + -1.04615, + -0.575, + -4.934228, + 5.633441, + -0.575, + -5.226083, + 5.868254, + -0.575, + -5.520293, + 6.004503, + -0.575, + -5.809068, + 6.059581, + -0.575, + -6.084616, + 6.050886, + -0.575, + -6.339147, + 5.995806, + -0.575, + -6.56487, + 5.911737, + -0.575, + -6.753994, + 5.816073, + -0.575, + -6.898729, + 5.726206, + -0.575, + -6.991283, + 5.659532, + -0.575, + -7.023865, + 5.633441, + -0.575, + -7.530505, + 5.123446, + -0.575, + -8.037145, + 4.613451, + -0.575, + -8.543784, + 4.103457, + -0.575, + -9.050424, + 3.593462, + -0.575, + -9.557063, + 3.083467, + -0.575, + -10.063703, + 2.573472, + -0.575, + -10.570343, + 2.063477, + -0.575, + -11.076982, + 1.553482, + -0.575, + -11.583623, + 1.043487, + -0.575, + -11.666567, + -0.962921, + -0.575, + -11.741323, + -0.873133, + -0.575, + -11.807673, + -0.777475, + -0.575, + -11.865397, + -0.676635, + -0.575, + -11.914281, + -0.571301, + -0.575, + -11.954103, + -0.462163, + -0.575, + -11.984652, + -0.349908, + -0.575, + -12.005705, + -0.235226, + -0.575, + -12.017047, + -0.118804, + -0.575, + -12.01846, + -0.001332, + -0.575, + -12.017047, + 0.116141, + -0.575, + -12.005705, + 0.232563, + -0.575, + -11.984652, + 0.347245, + -0.575, + -11.954103, + 0.4595, + -0.575, + -11.914281, + 0.568638, + -0.575, + -11.865397, + 0.673972, + -0.575, + -11.807673, + 0.774812, + -0.575, + -11.741323, + 0.87047, + -0.575, + -11.666567, + 0.960258, + -0.575, + 0, + 1.164275, + -0.575, + -0.067481, + 1.170574, + -0.575, + 0, + 1.164275, + 0.575, + -0.067481, + 1.170574, + 0.575, + -0.175364, + 1.185678, + -0.575, + -0.175364, + 1.185678, + 0.575, + -0.281, + 1.209335, + -0.575, + -0.281, + 1.209335, + 0.575, + -0.383989, + 1.24129, + -0.575, + -0.383989, + 1.24129, + 0.575, + -0.483936, + 1.281289, + -0.575, + -0.483936, + 1.281289, + 0.575, + -0.580439, + 1.329079, + -0.575, + -0.580439, + 1.329079, + 0.575, + -0.673102, + 1.384406, + -0.575, + -0.673102, + 1.384406, + 0.575, + -0.761525, + 1.447016, + -0.575, + -0.761525, + 1.447016, + 0.575, + -0.84531, + 1.516657, + -0.575, + -0.84531, + 1.516657, + 0.575, + -0.924058, + 1.593074, + -0.575, + -0.924058, + 1.593074, + 0.575, + -1.010416, + 1.680114, + -0.575, + -1.010416, + 1.680114, + 0.575, + -1.24893, + 1.920507, + -0.575, + -1.24893, + 1.920507, + 0.575, + -1.608765, + 2.283161, + -0.575, + -1.608765, + 2.283161, + 0.575, + -2.059081, + 2.736987, + -0.575, + -2.059081, + 2.736987, + 0.575, + -2.569043, + 3.250893, + -0.575, + -2.569043, + 3.250893, + 0.575, + -3.107813, + 3.793788, + -0.575, + -3.107813, + 3.793788, + 0.575, + -3.644554, + 4.334581, + -0.575, + -3.644554, + 4.334581, + 0.575, + -4.148428, + 4.842182, + -0.575, + -4.148428, + 4.842182, + 0.575, + -4.588599, + 5.285498, + -0.575, + -4.588599, + 5.285498, + 0.575, + -4.934228, + 5.633441, + -0.575, + -4.934228, + 5.633441, + 0.575, + -5.226083, + 5.868254, + -0.575, + -5.226083, + 5.868254, + 0.575, + -5.520293, + 6.004503, + -0.575, + -5.520293, + 6.004503, + 0.575, + -5.809068, + 6.059581, + -0.575, + -5.809068, + 6.059581, + 0.575, + -6.084616, + 6.050886, + -0.575, + -6.084616, + 6.050886, + 0.575, + -6.339147, + 5.995806, + -0.575, + -6.339147, + 5.995806, + 0.575, + -6.56487, + 5.911737, + -0.575, + -6.56487, + 5.911737, + 0.575, + -6.753994, + 5.816073, + -0.575, + -6.753994, + 5.816073, + 0.575, + -6.753994, + 5.816073, + -0.575, + -6.898729, + 5.726206, + -0.575, + -6.753994, + 5.816073, + 0.575, + -6.898729, + 5.726206, + 0.575, + -6.991283, + 5.659532, + -0.575, + -6.991283, + 5.659532, + 0.575, + -7.023865, + 5.633441, + -0.575, + -7.023865, + 5.633441, + 0.575, + -7.530505, + 5.123446, + -0.575, + -7.530505, + 5.123446, + 0.575, + -11.583623, + 1.043487, + -0.575, + -11.666567, + 0.960258, + -0.575, + -11.583623, + 1.043487, + 0.575, + -11.666567, + 0.960258, + 0.575, + -11.741323, + 0.87047, + -0.575, + -11.741323, + 0.87047, + 0.575, + -11.807673, + 0.774812, + -0.575, + -11.807673, + 0.774812, + 0.575, + -11.865397, + 0.673972, + -0.575, + -11.865397, + 0.673972, + 0.575, + -11.914281, + 0.568638, + -0.575, + -11.914281, + 0.568638, + 0.575, + -11.954103, + 0.4595, + -0.575, + -11.954103, + 0.4595, + 0.575, + -11.984652, + 0.347245, + -0.575, + -11.984652, + 0.347245, + 0.575, + -12.005705, + 0.232563, + -0.575, + -12.005705, + 0.232563, + 0.575, + -12.017047, + 0.116141, + -0.575, + -12.017047, + 0.116141, + 0.575, + -12.01846, + -0.001332, + -0.575, + -12.01846, + -0.001332, + 0.575, + -12.017047, + -0.118804, + -0.575, + -12.017047, + -0.118804, + 0.575, + -12.005705, + -0.235226, + -0.575, + -12.005705, + -0.235226, + 0.575, + -11.984652, + -0.349908, + -0.575, + -11.984652, + -0.349908, + 0.575, + -11.954103, + -0.462163, + -0.575, + -11.954103, + -0.462163, + 0.575, + -11.914281, + -0.571301, + -0.575, + -11.914281, + -0.571301, + 0.575, + -11.865397, + -0.676635, + -0.575, + -11.865397, + -0.676635, + 0.575, + -11.807673, + -0.777475, + -0.575, + -11.807673, + -0.777475, + 0.575, + -11.741323, + -0.873133, + -0.575, + -11.741323, + -0.873133, + 0.575, + -11.666567, + -0.962921, + -0.575, + -11.666567, + -0.962921, + 0.575, + -11.583623, + -1.04615, + -0.575, + -11.583623, + -1.04615, + 0.575, + -11.076983, + -1.556144, + -0.575, + -11.076983, + -1.556144, + 0.575, + -7.023865, + -5.636104, + -0.575, + -6.991283, + -5.662032, + -0.575, + -7.023865, + -5.636104, + 0.575, + -6.991283, + -5.662032, + 0.575, + -6.898729, + -5.72829, + -0.575, + -6.898729, + -5.72829, + 0.575, + -6.753994, + -5.817595, + -0.575, + -6.753994, + -5.817595, + 0.575, + -6.753994, + -5.817595, + -0.575, + -6.56487, + -5.912661, + -0.575, + -6.753994, + -5.817595, + 0.575, + -6.56487, + -5.912661, + 0.575, + -6.339147, + -5.996204, + -0.575, + -6.339147, + -5.996204, + 0.575, + -6.084616, + -6.050939, + -0.575, + -6.084616, + -6.050939, + 0.575, + -5.809068, + -6.059581, + -0.575, + -5.809068, + -6.059581, + 0.575, + -5.520293, + -6.004847, + -0.575, + -5.520293, + -6.004847, + 0.575, + -5.226083, + -5.869449, + -0.575, + -5.226083, + -5.869449, + 0.575, + -4.934228, + -5.636104, + -0.575, + -4.934228, + -5.636104, + 0.575, + -4.588599, + -5.289629, + -0.575, + -4.588599, + -5.289629, + 0.575, + -4.148428, + -4.847163, + -0.575, + -4.148428, + -4.847163, + 0.575, + -3.644554, + -4.339907, + -0.575, + -3.644554, + -4.339907, + 0.575, + -3.107813, + -3.79906, + -0.575, + -3.107813, + -3.79906, + 0.575, + -2.569043, + -3.25582, + -0.575, + -2.569043, + -3.25582, + 0.575, + -2.059081, + -2.741389, + -0.575, + -2.059081, + -2.741389, + 0.575, + -1.608765, + -2.286965, + -0.575, + -1.608765, + -2.286965, + 0.575, + -1.24893, + -1.923749, + -0.575, + -1.24893, + -1.923749, + 0.575, + -1.010416, + -1.682939, + -0.575, + -1.010416, + -1.682939, + 0.575, + -0.924058, + -1.595736, + -0.575, + -0.924058, + -1.595736, + 0.575, + -0.84531, + -1.51932, + -0.575, + -0.84531, + -1.51932, + 0.575, + -0.761525, + -1.449679, + -0.575, + -0.761525, + -1.449679, + 0.575, + -0.673102, + -1.387068, + -0.575, + -0.673102, + -1.387068, + 0.575, + -0.580439, + -1.331742, + -0.575, + -0.580439, + -1.331742, + 0.575, + -0.483936, + -1.283952, + -0.575, + -0.483936, + -1.283952, + 0.575, + -0.383989, + -1.243953, + -0.575, + -0.383989, + -1.243953, + 0.575, + -0.281, + -1.211998, + -0.575, + -0.281, + -1.211998, + 0.575, + -0.175364, + -1.188342, + -0.575, + -0.175364, + -1.188342, + 0.575, + -0.067481, + -1.173238, + -0.575, + -0.067481, + -1.173238, + 0.575, + 0, + -1.166939, + -0.575, + 0, + -1.166939, + 0.575, + -7.530505, + -5.126109, + 0.575, + -7.530505, + -5.126109, + -0.575, + -8.037145, + -4.616115, + 0.575, + -8.037145, + -4.616115, + -0.575, + -8.543784, + -4.10612, + 0.575, + -8.543784, + -4.10612, + -0.575, + -9.050424, + -3.596124, + 0.575, + -9.050424, + -3.596124, + -0.575, + -9.557063, + -3.086129, + 0.575, + -9.557063, + -3.086129, + -0.575, + -10.063703, + -2.576134, + 0.575, + -10.063703, + -2.576134, + -0.575, + -10.570343, + -2.066139, + 0.575, + -10.570343, + -2.066139, + -0.575, + -11.076982, + 1.553482, + 0.575, + -11.076982, + 1.553482, + -0.575, + -10.570343, + 2.063477, + 0.575, + -10.570343, + 2.063477, + -0.575, + -10.063703, + 2.573472, + 0.575, + -10.063703, + 2.573472, + -0.575, + -9.557063, + 3.083467, + 0.575, + -9.557063, + 3.083467, + -0.575, + -9.050424, + 3.593462, + 0.575, + -9.050424, + 3.593462, + -0.575, + -8.543784, + 4.103457, + 0.575, + -8.543784, + 4.103457, + -0.575, + -8.037145, + 4.613451, + 0.575, + -8.037145, + 4.613451, + -0.575, + 0, + 1.164275, + -0.575, + 0, + 1.164275, + 0.575, + 0.067481, + 1.170574, + -0.575, + 0.067481, + 1.170574, + 0.575, + 0.175364, + 1.185678, + -0.575, + 0.175364, + 1.185678, + 0.575, + 0.281, + 1.209335, + -0.575, + 0.281, + 1.209335, + 0.575, + 0.383989, + 1.24129, + -0.575, + 0.383989, + 1.24129, + 0.575, + 0.483936, + 1.281289, + -0.575, + 0.483936, + 1.281289, + 0.575, + 0.580439, + 1.329079, + -0.575, + 0.580439, + 1.329079, + 0.575, + 0.673102, + 1.384406, + -0.575, + 0.673102, + 1.384406, + 0.575, + 0.761525, + 1.447016, + -0.575, + 0.761525, + 1.447016, + 0.575, + 0.84531, + 1.516657, + -0.575, + 0.84531, + 1.516657, + 0.575, + 0.924058, + 1.593074, + -0.575, + 0.924058, + 1.593074, + 0.575, + 1.010416, + 1.680114, + -0.575, + 1.010416, + 1.680114, + 0.575, + 1.24893, + 1.920507, + -0.575, + 1.24893, + 1.920507, + 0.575, + 1.608765, + 2.283161, + -0.575, + 1.608765, + 2.283161, + 0.575, + 2.059081, + 2.736987, + -0.575, + 2.059081, + 2.736987, + 0.575, + 2.569043, + 3.250893, + -0.575, + 2.569043, + 3.250893, + 0.575, + 3.107813, + 3.793788, + -0.575, + 3.107813, + 3.793788, + 0.575, + 3.644554, + 4.334581, + -0.575, + 3.644554, + 4.334581, + 0.575, + 4.148428, + 4.842182, + -0.575, + 4.148428, + 4.842182, + 0.575, + 4.588599, + 5.285498, + -0.575, + 4.588599, + 5.285498, + 0.575, + 4.934228, + 5.633441, + -0.575, + 4.934228, + 5.633441, + 0.575, + 5.226083, + 5.868254, + -0.575, + 5.226083, + 5.868254, + 0.575, + 5.520293, + 6.004503, + -0.575, + 5.520293, + 6.004503, + 0.575, + 5.809068, + 6.059581, + -0.575, + 5.809068, + 6.059581, + 0.575, + 6.084616, + 6.050886, + -0.575, + 6.084616, + 6.050886, + 0.575, + 6.339147, + 5.995806, + -0.575, + 6.339147, + 5.995806, + 0.575, + 6.56487, + 5.911737, + -0.575, + 6.56487, + 5.911737, + 0.575, + 6.753994, + 5.816073, + -0.575, + 6.753994, + 5.816073, + 0.575, + 6.753994, + 5.816073, + -0.575, + 6.753994, + 5.816073, + 0.575, + 6.898729, + 5.726206, + -0.575, + 6.898729, + 5.726206, + 0.575, + 6.991283, + 5.659532, + -0.575, + 6.991283, + 5.659532, + 0.575, + 7.023865, + 5.633441, + -0.575, + 7.023865, + 5.633441, + 0.575, + 7.530505, + 5.123446, + -0.575, + 7.530505, + 5.123446, + 0.575, + 11.583623, + 1.043487, + -0.575, + 11.583623, + 1.043487, + 0.575, + 11.666567, + 0.960258, + -0.575, + 11.666567, + 0.960258, + 0.575, + 11.741323, + 0.87047, + -0.575, + 11.741323, + 0.87047, + 0.575, + 11.807673, + 0.774812, + -0.575, + 11.807673, + 0.774812, + 0.575, + 11.865397, + 0.673972, + -0.575, + 11.865397, + 0.673972, + 0.575, + 11.914281, + 0.568638, + -0.575, + 11.914281, + 0.568638, + 0.575, + 11.954103, + 0.4595, + -0.575, + 11.954103, + 0.4595, + 0.575, + 11.984652, + 0.347245, + -0.575, + 11.984652, + 0.347245, + 0.575, + 12.005705, + 0.232563, + -0.575, + 12.005705, + 0.232563, + 0.575, + 12.017047, + 0.116141, + -0.575, + 12.017047, + 0.116141, + 0.575, + 12.01846, + -0.001332, + -0.575, + 12.01846, + -0.001332, + 0.575, + 12.017047, + -0.118804, + -0.575, + 12.017047, + -0.118804, + 0.575, + 12.005705, + -0.235226, + -0.575, + 12.005705, + -0.235226, + 0.575, + 11.984652, + -0.349908, + -0.575, + 11.984652, + -0.349908, + 0.575, + 11.954103, + -0.462163, + -0.575, + 11.954103, + -0.462163, + 0.575, + 11.914281, + -0.571301, + -0.575, + 11.914281, + -0.571301, + 0.575, + 11.865397, + -0.676635, + -0.575, + 11.865397, + -0.676635, + 0.575, + 11.807673, + -0.777475, + -0.575, + 11.807673, + -0.777475, + 0.575, + 11.741323, + -0.873133, + -0.575, + 11.741323, + -0.873133, + 0.575, + 11.666567, + -0.962921, + -0.575, + 11.666567, + -0.962921, + 0.575, + 11.583623, + -1.04615, + -0.575, + 11.583623, + -1.04615, + 0.575, + 11.076983, + -1.556144, + -0.575, + 11.076983, + -1.556144, + 0.575, + 7.023865, + -5.636104, + -0.575, + 7.023865, + -5.636104, + 0.575, + 6.991283, + -5.662032, + -0.575, + 6.991283, + -5.662032, + 0.575, + 6.898729, + -5.72829, + -0.575, + 6.898729, + -5.72829, + 0.575, + 6.753994, + -5.817595, + -0.575, + 6.753994, + -5.817595, + 0.575, + 6.753994, + -5.817595, + -0.575, + 6.753994, + -5.817595, + 0.575, + 6.56487, + -5.912661, + -0.575, + 6.56487, + -5.912661, + 0.575, + 6.339147, + -5.996204, + -0.575, + 6.339147, + -5.996204, + 0.575, + 6.084616, + -6.050939, + -0.575, + 6.084616, + -6.050939, + 0.575, + 5.809068, + -6.059581, + -0.575, + 5.809068, + -6.059581, + 0.575, + 5.520293, + -6.004847, + -0.575, + 5.520293, + -6.004847, + 0.575, + 5.226083, + -5.869449, + -0.575, + 5.226083, + -5.869449, + 0.575, + 4.934228, + -5.636104, + -0.575, + 4.934228, + -5.636104, + 0.575, + 4.588599, + -5.289629, + -0.575, + 4.588599, + -5.289629, + 0.575, + 4.148428, + -4.847163, + -0.575, + 4.148428, + -4.847163, + 0.575, + 3.644554, + -4.339907, + -0.575, + 3.644554, + -4.339907, + 0.575, + 3.107813, + -3.79906, + -0.575, + 3.107813, + -3.79906, + 0.575, + 2.569043, + -3.25582, + -0.575, + 2.569043, + -3.25582, + 0.575, + 2.059081, + -2.741389, + -0.575, + 2.059081, + -2.741389, + 0.575, + 1.608765, + -2.286965, + -0.575, + 1.608765, + -2.286965, + 0.575, + 1.24893, + -1.923749, + -0.575, + 1.24893, + -1.923749, + 0.575, + 1.010416, + -1.682939, + -0.575, + 1.010416, + -1.682939, + 0.575, + 0.924058, + -1.595736, + -0.575, + 0.924058, + -1.595736, + 0.575, + 0.84531, + -1.51932, + -0.575, + 0.84531, + -1.51932, + 0.575, + 0.761525, + -1.449679, + -0.575, + 0.761525, + -1.449679, + 0.575, + 0.673102, + -1.387068, + -0.575, + 0.673102, + -1.387068, + 0.575, + 0.580439, + -1.331742, + -0.575, + 0.580439, + -1.331742, + 0.575, + 0.483936, + -1.283952, + -0.575, + 0.483936, + -1.283952, + 0.575, + 0.383989, + -1.243953, + -0.575, + 0.383989, + -1.243953, + 0.575, + 0.281, + -1.211998, + -0.575, + 0.281, + -1.211998, + 0.575, + 0.175364, + -1.188342, + -0.575, + 0.175364, + -1.188342, + 0.575, + 0.067481, + -1.173238, + -0.575, + 0.067481, + -1.173238, + 0.575, + 0, + -1.166939, + -0.575, + 0, + -1.166939, + 0.575, + 7.530505, + -5.126109, + 0.575, + 7.530505, + -5.126109, + -0.575, + 8.037145, + -4.616115, + 0.575, + 8.037145, + -4.616115, + -0.575, + 8.543784, + -4.10612, + 0.575, + 8.543784, + -4.10612, + -0.575, + 9.050424, + -3.596124, + 0.575, + 9.050424, + -3.596124, + -0.575, + 9.557063, + -3.086129, + 0.575, + 9.557063, + -3.086129, + -0.575, + 10.063703, + -2.576134, + 0.575, + 10.063703, + -2.576134, + -0.575, + 10.570343, + -2.066139, + 0.575, + 10.570343, + -2.066139, + -0.575, + 11.076982, + 1.553482, + 0.575, + 11.076982, + 1.553482, + -0.575, + 10.570343, + 2.063477, + 0.575, + 10.570343, + 2.063477, + -0.575, + 10.063703, + 2.573472, + 0.575, + 10.063703, + 2.573472, + -0.575, + 9.557063, + 3.083467, + 0.575, + 9.557063, + 3.083467, + -0.575, + 9.050424, + 3.593462, + 0.575, + 9.050424, + 3.593462, + -0.575, + 8.543784, + 4.103457, + 0.575, + 8.543784, + 4.103457, + -0.575, + 8.037145, + 4.613451, + 0.575, + 8.037145, + 4.613451, + -0.575, + -1.010416, + 1.680114, + 0.575, + -1.24893, + 1.920507, + 0.575, + -0.924058, + 1.593074, + 0.575, + -1.608765, + 2.283161, + 0.575, + -2.059081, + 2.736987, + 0.575, + -2.569043, + 3.250893, + 0.575, + -3.107813, + 3.793788, + 0.575, + -3.644554, + 4.334581, + 0.575, + -4.148428, + 4.842182, + 0.575, + -4.588599, + 5.285498, + 0.575, + -4.934228, + 5.633441, + 0.575, + -5.226083, + 5.868254, + 0.575, + -5.520293, + 6.004503, + 0.575, + -5.809068, + 6.059581, + 0.575, + -6.084616, + 6.050886, + 0.575, + -6.339147, + 5.995806, + 0.575, + -6.56487, + 5.911737, + 0.575, + -6.753994, + 5.816073, + 0.575, + -6.898729, + 5.726206, + 0.575, + -6.991283, + 5.659532, + 0.575, + -7.023865, + 5.633441, + 0.575, + -7.530505, + 5.123446, + 0.575, + -8.037145, + 4.613451, + 0.575, + -8.543784, + 4.103457, + 0.575, + -9.050424, + 3.593462, + 0.575, + -9.557063, + 3.083467, + 0.575, + -10.063703, + 2.573472, + 0.575, + -10.570343, + 2.063477, + 0.575, + -11.076982, + 1.553482, + 0.575, + -0.84531, + 1.516657, + 0.575, + -11.583623, + 1.043487, + 0.575, + -0.761525, + 1.447016, + 0.575, + -11.666567, + 0.960258, + 0.575, + -11.741323, + 0.87047, + 0.575, + -11.807673, + 0.774812, + 0.575, + -11.865397, + 0.673972, + 0.575, + -11.914281, + 0.568638, + 0.575, + -11.954103, + 0.4595, + 0.575, + -11.984652, + 0.347245, + 0.575, + -12.005705, + 0.232563, + 0.575, + -12.017047, + 0.116141, + 0.575, + -12.01846, + -0.001332, + 0.575, + -12.017047, + -0.118804, + 0.575, + -12.005705, + -0.235226, + 0.575, + -11.984652, + -0.349908, + 0.575, + -11.954103, + -0.462163, + 0.575, + -11.914281, + -0.571301, + 0.575, + -11.865397, + -0.676635, + 0.575, + -11.807673, + -0.777475, + 0.575, + -11.741323, + -0.873133, + 0.575, + -11.666567, + -0.962921, + 0.575, + -11.583623, + -1.04615, + 0.575, + -11.076983, + -1.556144, + 0.575, + -10.570343, + -2.066139, + 0.575, + -10.063703, + -2.576134, + 0.575, + -9.557063, + -3.086129, + 0.575, + -9.050424, + -3.596124, + 0.575, + -8.543784, + -4.10612, + 0.575, + -8.037145, + -4.616115, + 0.575, + -7.530505, + -5.126109, + 0.575, + -7.023865, + -5.636104, + 0.575, + -6.991283, + -5.662032, + 0.575, + -6.898729, + -5.72829, + 0.575, + -6.753994, + -5.817595, + 0.575, + -6.56487, + -5.912661, + 0.575, + -6.339147, + -5.996204, + 0.575, + -6.084616, + -6.050939, + 0.575, + -5.809068, + -6.059581, + 0.575, + -5.520293, + -6.004847, + 0.575, + -5.226083, + -5.869449, + 0.575, + -4.934228, + -5.636104, + 0.575, + -4.588599, + -5.289629, + 0.575, + -4.148428, + -4.847163, + 0.575, + -3.644554, + -4.339907, + 0.575, + -3.107813, + -3.79906, + 0.575, + -2.569043, + -3.25582, + 0.575, + -2.059081, + -2.741389, + 0.575, + -1.608765, + -2.286965, + 0.575, + -1.24893, + -1.923749, + 0.575, + -1.010416, + -1.682939, + 0.575, + -0.924058, + -1.595736, + 0.575, + -0.84531, + -1.51932, + 0.575, + -0.761525, + -1.449679, + 0.575, + -0.673102, + 1.384406, + 0.575, + -0.673102, + -1.387068, + 0.575, + -0.580439, + 1.329079, + 0.575, + -0.580439, + -1.331742, + 0.575, + -0.483936, + 1.281289, + 0.575, + -0.483936, + -1.283952, + 0.575, + -0.383989, + 1.24129, + 0.575, + -0.383989, + -1.243953, + 0.575, + -0.281, + 1.209335, + 0.575, + -0.281, + -1.211998, + 0.575, + -0.175364, + 1.185678, + 0.575, + -0.175364, + -1.188342, + 0.575, + -0.067481, + 1.170574, + 0.575, + -0.067481, + -1.173238, + 0.575, + -0.000028, + 1.164275, + 0.575, + -0.000028, + -1.166939, + 0.575, + 0.067427, + 1.170574, + 0.575, + 0.067427, + -1.173238, + 0.575, + 0.175309, + -1.188342, + 0.575, + 0.175309, + 1.185678, + 0.575, + 0.280944, + -1.211998, + 0.575, + 0.280944, + 1.209335, + 0.575, + 0.383935, + -1.243953, + 0.575, + 0.383935, + 1.24129, + 0.575, + 0.48388, + -1.283952, + 0.575, + 0.48388, + 1.281289, + 0.575, + 0.580384, + -1.331742, + 0.575, + 0.580384, + 1.329079, + 0.575, + 0.673046, + -1.387068, + 0.575, + 0.673046, + 1.384406, + 0.575, + 0.76147, + -1.449679, + 0.575, + 0.76147, + 1.447016, + 0.575, + 0.845254, + -1.51932, + 0.575, + 0.845254, + 1.516657, + 0.575, + 0.924003, + -1.595736, + 0.575, + 0.924003, + 1.593074, + 0.575, + 1.01036, + -1.682939, + 0.575, + 1.01036, + 1.680277, + 0.575, + 1.248874, + -1.923749, + 0.575, + 1.248874, + 1.921086, + 0.575, + 1.608709, + -2.286965, + 0.575, + 1.608709, + 2.284303, + 0.575, + 2.059026, + -2.741389, + 0.575, + 2.059026, + 2.738727, + 0.575, + 2.568988, + -3.25582, + 0.575, + 2.568988, + 3.253158, + 0.575, + 3.107758, + -3.79906, + 0.575, + 3.107758, + 3.796397, + 0.575, + 3.644499, + -4.339907, + 0.575, + 3.644499, + 4.337245, + 0.575, + 4.148373, + -4.847163, + 0.575, + 4.148373, + 4.844501, + 0.575, + 4.588543, + -5.289629, + 0.575, + 4.588543, + 5.286966, + 0.575, + 4.934174, + -5.636104, + 0.575, + 5.226027, + -5.869449, + 0.575, + 5.520238, + -6.004847, + 0.575, + 5.809012, + -6.059581, + 0.575, + 6.084561, + -6.050939, + 0.575, + 6.339092, + -5.996204, + 0.575, + 6.564814, + -5.912661, + 0.575, + 6.753939, + -5.817595, + 0.575, + 6.898673, + -5.72829, + 0.575, + 6.991227, + -5.662032, + 0.575, + 7.02381, + -5.636104, + 0.575, + 7.53045, + -5.126109, + 0.575, + 8.037088, + -4.616114, + 0.575, + 8.543729, + -4.106119, + 0.575, + 9.050367, + -3.596124, + 0.575, + 9.557008, + -3.086129, + 0.575, + 10.063648, + -2.576134, + 0.575, + 10.570287, + -2.066139, + 0.575, + 11.076928, + -1.556144, + 0.575, + 11.583566, + -1.04615, + 0.575, + 4.934174, + 5.633441, + 0.575, + 5.226027, + 5.866786, + 0.575, + 5.520238, + 6.002184, + 0.575, + 5.809012, + 6.056918, + 0.575, + 6.084561, + 6.048276, + 0.575, + 6.339092, + 5.993541, + 0.575, + 6.564814, + 5.909997, + 0.575, + 6.753939, + 5.814932, + 0.575, + 6.898673, + 5.725626, + 0.575, + 6.991227, + 5.659369, + 0.575, + 7.02381, + 5.633441, + 0.575, + 7.53045, + 5.123446, + 0.575, + 8.03709, + 4.613451, + 0.575, + 8.543729, + 4.103457, + 0.575, + 9.050369, + 3.593462, + 0.575, + 9.557008, + 3.083467, + 0.575, + 10.063648, + 2.573472, + 0.575, + 10.570287, + 2.063477, + 0.575, + 11.076928, + 1.553482, + 0.575, + 11.583566, + 1.043487, + 0.575, + 11.666512, + -0.962921, + 0.575, + 11.741269, + -0.873133, + 0.575, + 11.807617, + -0.777475, + 0.575, + 11.865342, + -0.676635, + 0.575, + 11.914224, + -0.571301, + 0.575, + 11.954048, + -0.462163, + 0.575, + 11.984597, + -0.349908, + 0.575, + 12.005651, + -0.235226, + 0.575, + 12.016992, + -0.118804, + 0.575, + 12.018405, + -0.001332, + 0.575, + 12.01846, + 0.116141, + 0.575, + 12.007968, + 0.232563, + 0.575, + 11.987259, + 0.347245, + 0.575, + 11.956657, + 0.4595, + 0.575, + 11.916491, + 0.568638, + 0.575, + 11.867082, + 0.673972, + 0.575, + 11.80876, + 0.774812, + 0.575, + 11.741847, + 0.87047, + 0.575, + 11.666675, + 0.960258, + 0.575 + ] + } + ] +} \ No newline at end of file diff --git a/privacy_policy.txt b/privacy_policy.txt new file mode 100755 index 0000000..56b0d61 --- /dev/null +++ b/privacy_policy.txt @@ -0,0 +1,16 @@ +This privacy policy describes how the MWC Wallet website, app, and extension use, share, and store personal information about its users. Please note that this privacy policy doesn't apply to information collected through third-party websites and services that you may interact with through the use of MWC Wallet's services. + +What Is Collected: +MWC Wallet doesn't actively collect any personal information about its users. + +What Is Automatically Collected: +MWC Wallet may automatically record certain information about how its users use its services in log files on its servers. This may include a user's IP address, device type, web browser type, operating system, the pages and features which they accessed, and the time which they accessed those pages and features. MWC Wallet may use this information to maintain the functionality of its services. + +What Is shared: +MWC Wallet doesn't share any personal information about its users. + +How Data Is Stored: +MWC Wallet uses local storage and similar technologies to store personal information locally on a user's machine. Some of this stored information is encrypted using a password provided by the user. + +What Cookies Are Used: +MWC Wallet only uses cookies that are essential to its functionality. These cookies are used to optimize resource loading, set preferences, and maintain a session with MWC Wallet's services. diff --git a/robots.txt b/robots.txt new file mode 100755 index 0000000..79ee58a --- /dev/null +++ b/robots.txt @@ -0,0 +1,20 @@ +# Sitemap +Sitemap: https:///sitemap.xml + +# Prevent and allow access +User-agent: * +Disallow: /errors/ +Disallow: /connection_test.html +Disallow: /tor/ +Disallow: /listen +Disallow: /wallet/ +Disallow: /donate/ +Disallow: /browser_extension_api_example.html +Disallow: /mwc_pay_example/ diff --git a/scripts/BLAKE2b license.txt b/scripts/BLAKE2b license.txt new file mode 100755 index 0000000..6d7451c --- /dev/null +++ b/scripts/BLAKE2b license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Nicolas Flamel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/BLAKE2b-0.0.2.js b/scripts/BLAKE2b-0.0.2.js new file mode 100755 index 0000000..b163bb4 --- /dev/null +++ b/scripts/BLAKE2b-0.0.2.js @@ -0,0 +1,181 @@ + +var blake2b = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + + return ( +function(blake2b) { + blake2b = blake2b || {}; + + +var a;a||(a=typeof blake2b !== 'undefined' ? blake2b : {});var g,h;a.ready=new Promise(function(b,d){g=b;h=d});var k=Object.assign({},a),m="";"undefined"!=typeof document&&document.currentScript&&(m=document.currentScript.src);_scriptDir&&(m=_scriptDir);0!==m.indexOf("blob:")?m=m.substr(0,m.replace(/[?#].*/,"").lastIndexOf("/")+1):m="";var n=a.printErr||console.warn.bind(console);Object.assign(a,k);k=null;var p;a.wasmBinary&&(p=a.wasmBinary);var noExitRuntime=a.noExitRuntime||!0; +"object"!=typeof WebAssembly&&q("no native wasm support detected");var r,t=!1,u,v;function w(){var b=r.buffer;u=b;a.HEAP8=new Int8Array(b);a.HEAP16=new Int16Array(b);a.HEAP32=new Int32Array(b);a.HEAPU8=v=new Uint8Array(b);a.HEAPU16=new Uint16Array(b);a.HEAPU32=new Uint32Array(b);a.HEAPF32=new Float32Array(b);a.HEAPF64=new Float64Array(b)}var x=[],y=[],z=[];function A(){var b=a.preRun.shift();x.unshift(b)}var B=0,C=null,D=null; +function q(b){if(a.onAbort)a.onAbort(b);b="Aborted("+b+")";n(b);t=!0;b=new WebAssembly.RuntimeError(b+". Build with -sASSERTIONS for more info.");h(b);throw b;}function E(){return F.startsWith("data:application/octet-stream;base64,")}var F;F="." + getResource("./scripts/BLAKE2b-0.0.2.wasm");if(!E()){var G=F;F=a.locateFile?a.locateFile(G,m):m+G}function H(){var b=F;try{if(b==F&&p)return new Uint8Array(p);throw"both async and sync fetching of the wasm failed";}catch(d){q(d)}} +function I(){return p||"function"!=typeof fetch?Promise.resolve().then(function(){return H()}):fetch(F,{credentials:"same-origin"}).then(function(b){if(!b.ok)throw"failed to load wasm binary file at '"+F+"'";return b.arrayBuffer()}).catch(function(){return H()})}function J(b){for(;0>>=0;if(2147483648=l;l*=2){var f=d*(1+.2/l);f=Math.min(f,b+100663296);var c=Math;f=Math.max(b,f);c=c.min.call(c,2147483648,f+(65536-f%65536)%65536);a:{try{r.grow(c-u.byteLength+65535>>>16);w();var e=1;break a}catch(O){}e=void 0}if(e)return!0}return!1}}; +(function(){function b(c){a.asm=c.exports;r=a.asm.b;w();y.unshift(a.asm.c);B--;a.monitorRunDependencies&&a.monitorRunDependencies(B);0==B&&(null!==C&&(clearInterval(C),C=null),D&&(c=D,D=null,c()))}function d(c){b(c.instance)}function l(c){return I().then(function(e){return WebAssembly.instantiate(e,f)}).then(function(e){return e}).then(c,function(e){n("failed to asynchronously prepare wasm: "+e);q(e)})}var f={a:K};B++;a.monitorRunDependencies&&a.monitorRunDependencies(B);if(a.instantiateWasm)try{return a.instantiateWasm(f, +b)}catch(c){return n("Module.instantiateWasm callback failed with error: "+c),!1}(function(){return p||"function"!=typeof WebAssembly.instantiateStreaming||E()||"function"!=typeof fetch?l(d):fetch(F,{credentials:"same-origin"}).then(function(c){return WebAssembly.instantiateStreaming(c,f).then(d,function(e){n("wasm streaming compile failed: "+e);n("falling back to ArrayBuffer instantiation");return l(d)})})})().catch(h);return{}})(); +a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.c).apply(null,arguments)};a._compute=function(){return(a._compute=a.asm.d).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.e).apply(null,arguments)};a._free=function(){return(a._free=a.asm.f).apply(null,arguments)};var L;D=function M(){L||N();L||(D=M)}; +function N(){function b(){if(!L&&(L=!0,a.calledRun=!0,!t)){J(y);g(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;){var d=a.postRun.shift();z.unshift(d)}J(z)}}if(!(0>> (32 - c)); +} +var constants = new Buffer('expand 32-byte k'); +module.exports = Chacha20; +function Chacha20(key, nonce) { + this.input = new Uint32Array(16); + + // https://tools.ietf.org/html/draft-irtf-cfrg-chacha20-poly1305-01#section-2.3 + this.input[0] = constants.readUInt32LE(0); + this.input[1] = constants.readUInt32LE(4); + this.input[2] = constants.readUInt32LE(8); + this.input[3] = constants.readUInt32LE(12); + this.input[4] = key.readUInt32LE(0); + this.input[5] = key.readUInt32LE(4); + this.input[6] = key.readUInt32LE(8); + this.input[7] = key.readUInt32LE(12); + this.input[8] = key.readUInt32LE(16); + this.input[9] = key.readUInt32LE(20); + this.input[10] = key.readUInt32LE(24); + this.input[11] = key.readUInt32LE(28); + + this.input[12] = 0; + + this.input[13] = nonce.readUInt32LE(0); + this.input[14] = nonce.readUInt32LE(4); + this.input[15] = nonce.readUInt32LE(8); + + this.cachePos = 64; + this.buffer = new Uint32Array(16); + this.output = new Buffer(64); +} + +Chacha20.prototype.quarterRound = function(a, b, c, d) { + var x = this.buffer; + x[a] += x[b]; x[d] = ROTATE(x[d] ^ x[a], 16); + x[c] += x[d]; x[b] = ROTATE(x[b] ^ x[c], 12); + x[a] += x[b]; x[d] = ROTATE(x[d] ^ x[a], 8); + x[c] += x[d]; x[b] = ROTATE(x[b] ^ x[c], 7); +}; +Chacha20.prototype.makeBlock = function (output, start) { + var i = -1; + // copy input into working buffer + while (++i < 16) { + this.buffer[i] = this.input[i]; + } + i = -1; + while (++i < 10) { + // straight round + this.quarterRound(0, 4, 8,12); + this.quarterRound(1, 5, 9,13); + this.quarterRound(2, 6,10,14); + this.quarterRound(3, 7,11,15); + + + //diaganle round + this.quarterRound(0, 5,10,15); + this.quarterRound(1, 6,11,12); + this.quarterRound(2, 7, 8,13); + this.quarterRound(3, 4, 9,14); + } + i = -1; + // copy working buffer into output + while (++i < 16) { + this.buffer[i] += this.input[i]; + output.writeUInt32LE(this.buffer[i], start); + start += 4; + } + + this.input[12]++; + if (!this.input[12]) { + throw new Error('counter is exausted'); + } +}; +Chacha20.prototype.getBytes = function(len) { + var dpos = 0; + var dst = new Buffer(len); + var cacheLen = 64 - this.cachePos; + if (cacheLen) { + if (cacheLen >= len) { + this.output.copy(dst, 0, this.cachePos, 64); + this.cachePos += len; + return dst; + } else { + this.output.copy(dst, 0, this.cachePos, 64); + len -= cacheLen; + dpos += cacheLen; + this.cachePos = 64; + } + } + while (len > 0 ) { + if (len <= 64) { + this.makeBlock(this.output, 0); + this.output.copy(dst, dpos, 0, len); + if (len < 64) { + this.cachePos = len; + } + return dst; + } else { + this.makeBlock(dst, dpos); + } + len -= 64; + dpos += 64; + } + throw new Error('something bad happended'); +}; +}).call(this)}).call(this,require("buffer").Buffer) +},{"buffer":27}],3:[function(require,module,exports){ +var Chacha20 = require('./chacha20'); +var inherits = require('inherits'); +var CipherBase = require('./cipherbase'); +inherits(ChaChaStream, CipherBase); +module.exports = ChaChaStream; +function ChaChaStream (key, iv) { + if (!(this instanceof ChaChaStream)) { + return new ChaChaStream(key, iv); + } + CipherBase.call(this); + this.chacha = new Chacha20(key, iv); +} +ChaChaStream.prototype._update = function (chunk) { + var len = chunk.length; + if (!len) { + return; + } + var pad = this.chacha.getBytes(len); + var i = -1; + while (++i < len) { + pad[i] ^= chunk[i]; + } + return pad; + }; +},{"./chacha20":2,"./cipherbase":4,"inherits":10}],4:[function(require,module,exports){ +(function (Buffer){(function (){ +var Transform = require('readable-stream').Transform; +var inherits = require('inherits'); + +module.exports = CipherBase; +inherits(CipherBase, Transform); +function CipherBase(digest) { + if (digest) { + this.digest = finalFunc; + } else { + this.final = finalFunc; + } + +} +[ + '_readableState', + '_writableState', + '_transformState' +].forEach(function(prop) { + Object.defineProperty(CipherBase.prototype, prop, { + get: function() { + Transform.call(this); + return this[prop]; + }, + set: function(val) { + Object.defineProperty(this, prop, { + value: val, + enumerable: true, + configurable: true, + writable: true + }); + }, + configurable: true, + enumerable: true + }); +}); +CipherBase.prototype.update = function (data, inputEnc, outputEnc) { + if (typeof data === 'string') { + data = new Buffer(data, inputEnc); + } + var outData = this._update(data) || new Buffer(''); + if (outputEnc) { + outData = outData.toString(outputEnc); + } + if (this.digest) { + return this; + } + return outData; +}; +CipherBase.prototype._transform = function (data, _, next) { + this.push(this._update(data)); + next(); +}; +CipherBase.prototype._flush = function (next) { + try { + this.push(this._final()); + } catch(e) { + return next(e); + } + next(); +}; +function finalFunc (outputEnc) { + var outData = this._final() || new Buffer(''); + if (outputEnc) { + outData = outData.toString(outputEnc); + } + return outData; +}; +CipherBase.prototype._final = function () {}; + +}).call(this)}).call(this,require("buffer").Buffer) +},{"buffer":27,"inherits":10,"readable-stream":21}],5:[function(require,module,exports){ +var Cipher = require('./aead'); +exports.aead = Cipher; +exports.createCipher = createCipher; +function createCipher(key, iv) { + return new Cipher(key, iv); +} +exports.createDecipher = createDecipher; +function createDecipher(key, iv) { + return new Cipher(key, iv, true); +} + +exports.createHmac = require('./polystream'); +exports.chacha20 = exports.ChaCha20 = require('./chachastream'); +exports.aeadLegacy = exports.AeadLegacy = require('./legacy-aead'); +},{"./aead":1,"./chachastream":3,"./legacy-aead":6,"./polystream":8}],6:[function(require,module,exports){ +(function (Buffer){(function (){ +var inherits = require('inherits'); +var CipherBase = require('./cipherbase'); +var Chacha20 = require('./chacha20'); +var Poly1305 = require('./poly1305'); +inherits(Cipher, CipherBase); +module.exports = Cipher; + var zeros = new Buffer (4); + zeros.fill(0); +function Cipher(key, iv, decrypt){ + if (!(this instanceof Cipher)) { + return new Cipher(key, iv, decrypt); + } + CipherBase.call(this); + this.alen = 0; + this.clen = 0; + this.chacha = new Chacha20(key, Buffer.concat([zeros,iv])); + this.poly = new Poly1305(this.chacha.getBytes(64)); + this.tag = null; + this._decrypt = decrypt; + this._hasData = false; +} +Cipher.prototype.setAAD = function (aad) { + if (this._hasData) { + throw new Error('Attempting to set AAD in unsupported state'); + } + this.alen += aad.length; + this.poly.update(aad); + // var padding = new Buffer(padAmount(this.alen)); + // if (padding.length) { + // padding.fill(0); + // this.poly.update(padding); + // } + //this.poly.update(len); +}; +Cipher.prototype._flushlentag = function () { + this._hasData = true; + var len = new Buffer(8); + len.fill(0); + len.writeUInt32LE(this.alen, 0); + this.poly.update(len); +}; +Cipher.prototype._update = function (chunk) { + if (!this._hasData) { + this._flushlentag(); + } + var len = chunk.length; + if (!len) { + return; + } + this.clen += len; + var pad = this.chacha.getBytes(len); + var i = -1; + while (++i < len) { + pad[i] ^= chunk[i]; + } + if (this._decrypt) { + this.poly.update(chunk); + } else { + this.poly.update(pad); + } + return pad; +}; +Cipher.prototype._final = function () { + if (this._decrypt && !this.tag) { + throw new Error('Unsupported state or unable to authenticate data'); + } + if (!this._hasData) { + this._flushlentag(); + } + // var padding = new Buffer(padAmount(this.clen)); + // if (padding.length) { + // padding.fill(0); + // this.poly.update(padding); + // } + var lens = new Buffer(8); + lens.fill(0); + //lens.writeUInt32LE(this.alen, 0); + lens.writeUInt32LE(this.clen, 0); + var tag = this.poly.update(lens).finish(); + if (this._decrypt) { + if (xorTest(tag, this.tag)) { + throw new Error('Unsupported state or unable to authenticate data'); + } + } else { + this.tag = tag; + } +}; +Cipher.prototype.getAuthTag = function () { + if(this._decrypt || this.tag === null) { + throw new Error('Attempting to get auth tag in unsupported state'); + } + return this.tag; +}; +Cipher.prototype.setAuthTag = function setAuthTag (tag) { + if (this._decrypt) { + this.tag = tag; + } else { + throw new Error('Attempting to set auth tag in unsupported state'); + } +}; +function padAmount(len) { + var rem = len % 16; + if (rem === 16) { + return 0; + } + return 16 - rem; +} +function xorTest(a, b) { + var out = 0; + if (a.length !== b.length) { + out++; + } + var len = Math.min(a.length, b.length); + var i = -1; + while (++i < len) { + out += (a[i] ^ b[i]); + } + return out; +} + +}).call(this)}).call(this,require("buffer").Buffer) +},{"./chacha20":2,"./cipherbase":4,"./poly1305":7,"buffer":27,"inherits":10}],7:[function(require,module,exports){ +(function (Buffer){(function (){ +var Poly1305KeySize = 32; +var Poly1305TagSize = 16; +module.exports = Poly1305; +function Poly1305(key) { + if (!(this instanceof Poly1305)) { + return new Poly1305(key); + } + this.buffer = new Buffer(16); + this.leftover = 0; + this.r = new Uint16Array(10); + this.h = new Uint16Array(10); + this.pad = new Uint16Array(8); + this.finished = 0; + + var t = new Uint16Array(8), i; + + for (i = 8; i--;) t[i] = key.readUInt16LE(i*2); + + this.r[0] = t[0] & 0x1fff; + this.r[1] = ((t[0] >>> 13) | (t[1] << 3)) & 0x1fff; + this.r[2] = ((t[1] >>> 10) | (t[2] << 6)) & 0x1f03; + this.r[3] = ((t[2] >>> 7) | (t[3] << 9)) & 0x1fff; + this.r[4] = ((t[3] >>> 4) | (t[4] << 12)) & 0x00ff; + this.r[5] = (t[4] >>> 1) & 0x1ffe; + this.r[6] = ((t[4] >>> 14) | (t[5] << 2)) & 0x1fff; + this.r[7] = ((t[5] >>> 11) | (t[6] << 5)) & 0x1f81; + this.r[8] = ((t[6] >>> 8) | (t[7] << 8)) & 0x1fff; + this.r[9] = (t[7] >>> 5) & 0x007f; + + for (i = 8; i--;) { + this.h[i] = 0; + this.pad[i] = key.readUInt16LE( 16+(2*i)); + } + this.h[8] = 0; + this.h[9] = 0; + this.leftover = 0; + this.finished = 0; +} + + +function U16TO8_LE(p, pos, v) { + p[pos] = v; + p[pos+1] = v >>> 8; +} + +Poly1305.prototype.blocks = function(m, mpos, bytes) { + var hibit = this.finished ? 0 : (1 << 11); + var t = new Uint16Array(8), + d = new Uint32Array(10), + c = 0, i = 0, j = 0; + + while (bytes >= 16) { + for (i = 8; i--;) t[i] = m.readUInt16LE(i*2+mpos); + + this.h[0] += t[0] & 0x1fff; + this.h[1] += ((t[0] >>> 13) | (t[1] << 3)) & 0x1fff; + this.h[2] += ((t[1] >>> 10) | (t[2] << 6)) & 0x1fff; + this.h[3] += ((t[2] >>> 7) | (t[3] << 9)) & 0x1fff; + this.h[4] += ((t[3] >>> 4) | (t[4] << 12)) & 0x1fff; + this.h[5] += (t[4] >>> 1) & 0x1fff; + this.h[6] += ((t[4] >>> 14) | (t[5] << 2)) & 0x1fff; + this.h[7] += ((t[5] >>> 11) | (t[6] << 5)) & 0x1fff; + this.h[8] += ((t[6] >>> 8) | (t[7] << 8)) & 0x1fff; + this.h[9] += (t[7] >>> 5) | hibit; + + for (i = 0, c = 0; i < 10; i++) { + d[i] = c; + for (j = 0; j < 10; j++) { + d[i] += (this.h[j] & 0xffffffff) * ((j <= i) ? this.r[i-j] : (5 * this.r[i+10-j])); + if (j === 4) { + c = (d[i] >>> 13); + d[i] &= 0x1fff; + } + } + c += (d[i] >>> 13); + d[i] &= 0x1fff; + } + c = ((c << 2) + c); + c += d[0]; + d[0] = ((c & 0xffff) & 0x1fff); + c = (c >>> 13); + d[1] += c; + + for (i = 10; i--;) this.h[i] = d[i]; + + mpos += 16; + bytes -= 16; + } +}; + +Poly1305.prototype.update = function(m) { + var bytes = m.length; + var want = 0, i = 0, mpos = 0; + // var chunk; + // this.buffer = Buffer.concat([this.buffer, m]); + // while (this.buffer.length >= 16) { + // chunk = this.buffer.slice(0, 16); + // this.buffer = this.buffer.slice(16) + // this.blocks(chunk, 0, 16); + // } + if (this.leftover) { + want = 16 - this.leftover; + if (want > bytes) + want = bytes; + for (i = want; i--;) { + this.buffer[this.leftover+i] = m[i+mpos]; + } + bytes -= want; + mpos += want; + this.leftover += want; + if (this.leftover < 16) + return this; + this.blocks(this.buffer, 0, 16); + this.leftover = 0; + } + + if (bytes >= 16) { + want = (bytes & ~(16 - 1)); + this.blocks(m, mpos, want); + mpos += want; + bytes -= want; + } + + if (bytes) { + for (i = bytes; i--;) { + this.buffer[this.leftover+i] = m[i+mpos]; + } + this.leftover += bytes; + } + return this; +}; + +Poly1305.prototype.finish = function() { + var mac = new Buffer(16), + g = new Uint16Array(10), + c = 0, mask = 0, f = 0, i = 0; + if (this.leftover) { + i = this.leftover; + this.buffer[i++] = 1; + for (; i < 16; i++) { + this.buffer[i] = 0; + } + this.finished = 1; + this.blocks(this.buffer, 0, 16); + } + + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + for (i = 2; i < 10; i++) { + this.h[i] += c; + c = this.h[i] >>> 13; + this.h[i] &= 0x1fff; + } + this.h[0] += (c * 5); + c = this.h[0] >>> 13; + this.h[0] &= 0x1fff; + this.h[1] += c; + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + this.h[2] += c; + + g[0] = this.h[0] + 5; + c = g[0] >>> 13; + g[0] &= 0x1fff; + for (i = 1; i < 10; i++) { + g[i] = this.h[i] + c; + c = g[i] >>> 13; + g[i] &= 0x1fff; + } + g[9] -= (1 << 13); + + mask = (g[9] >>> 15) - 1; + for (i = 10; i--;) g[i] &= mask; + mask = ~mask; + for (i = 10; i--;) { + this.h[i] = (this.h[i] & mask) | g[i]; + } + + this.h[0] = (this.h[0] ) | (this.h[1] << 13); + this.h[1] = (this.h[1] >> 3) | (this.h[2] << 10); + this.h[2] = (this.h[2] >> 6) | (this.h[3] << 7); + this.h[3] = (this.h[3] >> 9) | (this.h[4] << 4); + this.h[4] = (this.h[4] >> 12) | (this.h[5] << 1) | (this.h[6] << 14); + this.h[5] = (this.h[6] >> 2) | (this.h[7] << 11); + this.h[6] = (this.h[7] >> 5) | (this.h[8] << 8); + this.h[7] = (this.h[8] >> 8) | (this.h[9] << 5); + + f = (this.h[0] & 0xffffffff) + this.pad[0]; + this.h[0] = f; + for (i = 1; i < 8; i++) { + f = (this.h[i] & 0xffffffff) + this.pad[i] + (f >>> 16); + this.h[i] = f; + } + + for (i = 8; i--;) { + mac.writeUInt16LE(this.h[i], i*2); + this.pad[i] = 0; + } + for (i = 10; i--;) { + this.h[i] = 0; + this.r[i] = 0; + } + + return mac; +}; +}).call(this)}).call(this,require("buffer").Buffer) +},{"buffer":27}],8:[function(require,module,exports){ +var inherits = require('inherits'); + +var CipherBase = require('./cipherbase'); +var Poly1305 = require('./poly1305'); +module.exports = PolyStream; +inherits(PolyStream, CipherBase); +function PolyStream (key) { + if (!(this instanceof PolyStream)) { + return new PolyStream(key); + } + CipherBase.call(this, true); + this.poly = new Poly1305(key); +} +PolyStream.prototype._update = function (data) { + this.poly.update(data); +}; + +PolyStream.prototype._final = function () { + return this.poly.finish(); +}; + +},{"./cipherbase":4,"./poly1305":7,"inherits":10}],9:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('buffer').Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +},{"buffer":27}],10:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} + +},{}],11:[function(require,module,exports){ +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + +},{}],12:[function(require,module,exports){ +(function (process){(function (){ +'use strict'; + +if (typeof process === 'undefined' || + !process.version || + process.version.indexOf('v0.') === 0 || + process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { + module.exports = { nextTick: nextTick }; +} else { + module.exports = process +} + +function nextTick(fn, arg1, arg2, arg3) { + if (typeof fn !== 'function') { + throw new TypeError('"callback" argument must be a function'); + } + var len = arguments.length; + var args, i; + switch (len) { + case 0: + case 1: + return process.nextTick(fn); + case 2: + return process.nextTick(function afterTickOne() { + fn.call(null, arg1); + }); + case 3: + return process.nextTick(function afterTickTwo() { + fn.call(null, arg1, arg2); + }); + case 4: + return process.nextTick(function afterTickThree() { + fn.call(null, arg1, arg2, arg3); + }); + default: + args = new Array(len - 1); + i = 0; + while (i < args.length) { + args[i++] = arguments[i]; + } + return process.nextTick(function afterTick() { + fn.apply(null, args); + }); + } +} + + +}).call(this)}).call(this,require('_process')) +},{"_process":30}],13:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +module.exports = Duplex; + +/**/ +var util = Object.create(require('core-util-is')); +util.inherits = require('inherits'); +/**/ + +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +{ + // avoid scope creep, the keys array can then be collected + var keys = objectKeys(Writable.prototype); + for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} + +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) this.readable = false; + + if (options && options.writable === false) this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; + + this.once('end', onend); +} + +Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; + } +}); + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) return; + + // no more data can be written. + // But allow more writes to happen in this tick. + pna.nextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +Object.defineProperty(Duplex.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); + +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); + + pna.nextTick(cb, err); +}; +},{"./_stream_readable":15,"./_stream_writable":17,"core-util-is":9,"inherits":10,"process-nextick-args":12}],14:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +'use strict'; + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); + +/**/ +var util = Object.create(require('core-util-is')); +util.inherits = require('inherits'); +/**/ + +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":16,"core-util-is":9,"inherits":10}],15:[function(require,module,exports){ +(function (process,global){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +module.exports = Readable; + +/**/ +var isArray = require('isarray'); +/**/ + +/**/ +var Duplex; +/**/ + +Readable.ReadableState = ReadableState; + +/**/ +var EE = require('events').EventEmitter; + +var EElistenerCount = function (emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +/**/ + +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +/**/ + +/**/ +var util = Object.create(require('core-util-is')); +util.inherits = require('inherits'); +/**/ + +/**/ +var debugUtil = require('util'); +var debug = void 0; +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ + +var BufferList = require('./internal/streams/BufferList'); +var destroyImpl = require('./internal/streams/destroy'); +var StringDecoder; + +util.inherits(Readable, Stream); + +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); + + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; +} + +function ReadableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var readableHwm = options.readableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + + // has it been destroyed + this.destroyed = false; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + if (!(this instanceof Readable)) return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } + + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; + } + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + } +}); + +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } + + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; + +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + var state = stream._readableState; + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (addToFront) { + if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); + } else if (state.ended) { + stream.emit('error', new Error('stream.push() after EOF')); + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + } + } + + return needMoreData(state); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); +} + +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; + +// backwards compatibility. +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; +}; + +// Don't raise the hwm > 8MB +var MAX_HWM = 0x800000; +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + return n; +} + +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } + // If we're asking for more than the current hwm, then raise the hwm. + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; + // Don't have enough + if (!state.ended) { + state.needReadable = true; + return 0; + } + return state.length; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; + + if (n !== 0) state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) n = howMuchToRead(nOrig, state); + } + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = true; + n = 0; + } else { + state.length -= n; + } + + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this); + } + + if (ret !== null) this.emit('data', ret); + + return ret; +}; + +function onEofChunk(stream, state) { + if (state.ended) return; + if (state.decoder) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + pna.nextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break;else len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function (n) { + this.emit('error', new Error('_read() is not implemented')); +}; + +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + var cleanedUp = false; + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + + cleanedUp = true; + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + + // If the user pushes more data while we're writing to dest then we'll end up + // in ondata again. However, we only want to increase awaitDrain once because + // dest will only emit one 'drain' event for the multiple writes. + // => Introduce a guard on increasing awaitDrain. + var increasedAwaitDrain = false; + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + increasedAwaitDrain = false; + var ret = dest.write(chunk); + if (false === ret && !increasedAwaitDrain) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', src._readableState.awaitDrain); + src._readableState.awaitDrain++; + increasedAwaitDrain = true; + } + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); + } + + // Make sure our error handler is attached before userland ones. + prependListener(dest, 'error', onerror); + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function () { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; + + if (!dest) dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, unpipeInfo); + }return this; + } + + // try to find the right one. + var index = indexOf(state.pipes, dest); + if (index === -1) return this; + + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + + dest.emit('unpipe', this, unpipeInfo); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + if (ev === 'data') { + // Start flowing on next tick if stream isn't explicitly paused + if (this._readableState.flowing !== false) this.resume(); + } else if (ev === 'readable') { + var state = this._readableState; + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.emittedReadable = false; + if (!state.reading) { + pna.nextTick(nReadingNextTick, this); + } else if (state.length) { + emitReadable(this); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function () { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + resume(this, state); + } + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + pna.nextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + if (!state.reading) { + debug('resume read 0'); + stream.read(0); + } + + state.resumeScheduled = false; + state.awaitDrain = 0; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + while (state.flowing && stream.read() !== null) {} +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function (stream) { + var _this = this; + + var state = this._readableState; + var paused = false; + + stream.on('end', function () { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) _this.push(chunk); + } + + _this.push(null); + }); + + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); + + // don't skip over falsy values in objectMode + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = _this.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function (method) { + return function () { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } + + // proxy certain important events. + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); + } + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + this._read = function (n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return this; +}; + +Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._readableState.highWaterMark; + } +}); + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = fromListPartial(n, state.buffer, state.decoder); + } + + return ret; +} + +// Extracts only enough buffered data to satisfy the amount requested. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromListPartial(n, list, hasStrings) { + var ret; + if (n < list.head.data.length) { + // slice is the same for buffers and strings + ret = list.head.data.slice(0, n); + list.head.data = list.head.data.slice(n); + } else if (n === list.head.data.length) { + // first chunk is a perfect match + ret = list.shift(); + } else { + // result spans more than one buffer + ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); + } + return ret; +} + +// Copies a specified amount of characters from the list of buffered data +// chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBufferString(n, list) { + var p = list.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = str.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +// Copies a specified amount of bytes from the list of buffered data chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBuffer(n, list) { + var ret = Buffer.allocUnsafe(n); + var p = list.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = buf.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + pna.nextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } +} + +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; +} +}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./_stream_duplex":13,"./internal/streams/BufferList":18,"./internal/streams/destroy":19,"./internal/streams/stream":20,"_process":30,"core-util-is":9,"events":28,"inherits":10,"isarray":11,"process-nextick-args":12,"safe-buffer":22,"string_decoder/":23,"util":26}],16:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +'use strict'; + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); + +/**/ +var util = Object.create(require('core-util-is')); +util.inherits = require('inherits'); +/**/ + +util.inherits(Transform, Duplex); + +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) { + return this.emit('error', new Error('write callback called multiple times')); + } + + ts.writechunk = null; + ts.writecb = null; + + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); + + cb(er); + + var rs = this._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} + +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + + Duplex.call(this, options); + + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + + if (typeof options.flush === 'function') this._flush = options.flush; + } + + // When the writable side finishes, then flush out anything remaining. + this.on('prefinish', prefinish); +} + +function prefinish() { + var _this = this; + + if (typeof this._flush === 'function') { + this._flush(function (er, data) { + done(_this, er, data); + }); + } else { + done(this, null, null); + } +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function (chunk, encoding, cb) { + throw new Error('_transform() is not implemented'); +}; + +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function (n) { + var ts = this._transformState; + + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + +Transform.prototype._destroy = function (err, cb) { + var _this2 = this; + + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this2.emit('close'); + }); +}; + +function done(stream, er, data) { + if (er) return stream.emit('error', er); + + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); + + if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); + + return stream.push(null); +} +},{"./_stream_duplex":13,"core-util-is":9,"inherits":10}],17:[function(require,module,exports){ +(function (process,global,setImmediate){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +module.exports = Writable; + +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} + +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; +/**/ + +/**/ +var Duplex; +/**/ + +Writable.WritableState = WritableState; + +/**/ +var util = Object.create(require('core-util-is')); +util.inherits = require('inherits'); +/**/ + +/**/ +var internalUtil = { + deprecate: require('util-deprecate') +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +/**/ + +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +/**/ + +var destroyImpl = require('./internal/streams/destroy'); + +util.inherits(Writable, Stream); + +function nop() {} + +function WritableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var writableHwm = options.writableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // if _final has been called + this.finalCalled = false; + + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // has it been destroyed + this.destroyed = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.bufferedRequest = null; + this.lastBufferedRequest = null; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; + + // count buffered requests + this.bufferedRequestCount = 0; + + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; + } + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); + +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; + + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); + } + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + + if (typeof options.writev === 'function') this._writev = options.writev; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; + +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + pna.nextTick(cb, er); +} + +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; + + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + if (er) { + stream.emit('error', er); + pna.nextTick(cb, er); + valid = false; + } + return valid; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = !state.objectMode && _isUint8Array(chunk); + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + + if (typeof cb !== 'function') cb = nop; + + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function () { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + return chunk; +} + +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; + } +}); + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + pna.nextTick(cb, er); + // this can emit finish, and it will always happen + // after error + pna.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + buffer.allBuffers = allBuffers; + + doWrite(stream, state, true, state.length, buffer, '', holder.finish); + + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + pna.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); + } + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; + } +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; + } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; + } +}); + +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); +}; +}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) +},{"./_stream_duplex":13,"./internal/streams/destroy":19,"./internal/streams/stream":20,"_process":30,"core-util-is":9,"inherits":10,"process-nextick-args":12,"safe-buffer":22,"timers":31,"util-deprecate":24}],18:[function(require,module,exports){ +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Buffer = require('safe-buffer').Buffer; +var util = require('util'); + +function copyBuffer(src, target, offset) { + src.copy(target, offset); +} + +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; + + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; + + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; + + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; + + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; + + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + return ret; + }; + + return BufferList; +}(); + +if (util && util.inspect && util.inspect.custom) { + module.exports.prototype[util.inspect.custom] = function () { + var obj = util.inspect({ length: this.length }); + return this.constructor.name + ' ' + obj; + }; +} +},{"safe-buffer":22,"util":26}],19:[function(require,module,exports){ +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + pna.nextTick(emitErrorNT, this, err); + } + return this; + } + + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + if (this._readableState) { + this._readableState.destroyed = true; + } + + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + pna.nextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; + } + } else if (cb) { + cb(err); + } + }); + + return this; +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy +}; +},{"process-nextick-args":12}],20:[function(require,module,exports){ +module.exports = require('events').EventEmitter; + +},{"events":28}],21:[function(require,module,exports){ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); + +},{"./lib/_stream_duplex.js":13,"./lib/_stream_passthrough.js":14,"./lib/_stream_readable.js":15,"./lib/_stream_transform.js":16,"./lib/_stream_writable.js":17}],22:[function(require,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = require('buffer') +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + +},{"buffer":27}],23:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var Buffer = require('safe-buffer').Buffer; +/**/ + +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; + +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; + } + } +}; + +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} + +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; + +StringDecoder.prototype.end = utf8End; + +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; + +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} + +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} + +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} + +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} + +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} + +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} +},{"safe-buffer":22}],24:[function(require,module,exports){ +(function (global){(function (){ + +/** + * Module exports. + */ + +module.exports = deprecate; + +/** + * Mark that a method should not be used. + * Returns a modified function which warns once by default. + * + * If `localStorage.noDeprecation = true` is set, then it is a no-op. + * + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public + */ + +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +} + +/** + * Checks `localStorage` for boolean values for the given `name`. + * + * @param {String} name + * @returns {Boolean} + * @api private + */ + +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { + return false; + } + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; +} + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],25:[function(require,module,exports){ +'use strict' + +exports.byteLength = byteLength +exports.toByteArray = toByteArray +exports.fromByteArray = fromByteArray + +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i +} + +// Support decoding URL-safe base64 strings, as Node.js does. +// See: https://en.wikipedia.org/wiki/Base64#URL_applications +revLookup['-'.charCodeAt(0)] = 62 +revLookup['_'.charCodeAt(0)] = 63 + +function getLens (b64) { + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + var i + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} + +},{}],26:[function(require,module,exports){ + +},{}],27:[function(require,module,exports){ +(function (Buffer){(function (){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +var base64 = require('base64-js') +var ieee754 = require('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +var K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport () { + // Can typed array instances can be augmented? + try { + var arr = new Uint8Array(1) + arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } + return arr.foo() === 42 + } catch (e) { + return false + } +} + +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.buffer + } +}) + +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.byteOffset + } +}) + +function createBuffer (length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"') + } + // Return an augmented `Uint8Array` instance + var buf = new Uint8Array(length) + buf.__proto__ = Buffer.prototype + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 +if (typeof Symbol !== 'undefined' && Symbol.species != null && + Buffer[Symbol.species] === Buffer) { + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true, + enumerable: false, + writable: false + }) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from (value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + if (ArrayBuffer.isView(value)) { + return fromArrayLike(value) + } + + if (value == null) { + throw TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) + } + + if (isInstance(value, ArrayBuffer) || + (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'number') { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ) + } + + var valueOf = value.valueOf && value.valueOf() + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length) + } + + var b = fromObject(value) + if (b) return b + + if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function') { + return Buffer.from( + value[Symbol.toPrimitive]('string'), encodingOrOffset, length + ) + } + + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Buffer.prototype.__proto__ = Uint8Array.prototype +Buffer.__proto__ = Uint8Array + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number') + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } +} + +function alloc (size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe (size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString (string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + + var length = byteLength(string, encoding) | 0 + var buf = createBuffer(length) + + var actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike (array) { + var length = array.length < 0 ? 0 : checked(array.length) | 0 + var buf = createBuffer(length) + for (var i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayBuffer (array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds') + } + + var buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + buf.__proto__ = Buffer.prototype + return buf +} + +function fromObject (obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + var buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } +} + +function checked (length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return b != null && b._isBuffer === true && + b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false +} + +Buffer.compare = function compare (a, b) { + if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) + if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ) + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + var buffer = Buffer.allocUnsafe(length) + var pos = 0 + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (isInstance(buf, Uint8Array)) { + buf = Buffer.from(buf) + } + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + typeof string + ) + } + + var len = string.length + var mustMatch = (arguments.length > 2 && arguments[2] === true) + if (!mustMatch && len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 + } + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + var length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.toLocaleString = Buffer.prototype.toString + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() + if (this.length > max) str += ' ... ' + return '' +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength) + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. ' + + 'Received type ' + (typeof target) + ) + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { + var foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + var strLen = string.length + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; ++i) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function latin1Write (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'latin1': + case 'binary': + return latin1Write(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; ++i) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + newBuf.__proto__ = Buffer.prototype + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end) + } else if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (var i = len - 1; i >= 0; --i) { + target[i + targetStart] = this[i + start] + } + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if ((encoding === 'utf8' && code < 128) || + encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code + } + } + } else if (typeof val === 'number') { + val = val & 255 + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + var i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + var bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val, encoding) + var len = bytes.length + if (len === 0) { + throw new TypeError('The value "' + val + + '" is invalid for argument "value"') + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// HELPER FUNCTIONS +// ================ + +var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0] + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance (obj, type) { + return obj instanceof type || + (obj != null && obj.constructor != null && obj.constructor.name != null && + obj.constructor.name === type.name) +} +function numberIsNaN (obj) { + // For IE11 support + return obj !== obj // eslint-disable-line no-self-compare +} + +}).call(this)}).call(this,require("buffer").Buffer) +},{"base64-js":25,"buffer":27,"ieee754":29}],28:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +var R = typeof Reflect === 'object' ? Reflect : null +var ReflectApply = R && typeof R.apply === 'function' + ? R.apply + : function ReflectApply(target, receiver, args) { + return Function.prototype.apply.call(target, receiver, args); + } + +var ReflectOwnKeys +if (R && typeof R.ownKeys === 'function') { + ReflectOwnKeys = R.ownKeys +} else if (Object.getOwnPropertySymbols) { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target) + .concat(Object.getOwnPropertySymbols(target)); + }; +} else { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target); + }; +} + +function ProcessEmitWarning(warning) { + if (console && console.warn) console.warn(warning); +} + +var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { + return value !== value; +} + +function EventEmitter() { + EventEmitter.init.call(this); +} +module.exports = EventEmitter; +module.exports.once = once; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +var defaultMaxListeners = 10; + +function checkListener(listener) { + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } +} + +Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { + throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); + } + defaultMaxListeners = arg; + } +}); + +EventEmitter.init = function() { + + if (this._events === undefined || + this._events === Object.getPrototypeOf(this)._events) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +}; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { + throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); + } + this._maxListeners = n; + return this; +}; + +function _getMaxListeners(that) { + if (that._maxListeners === undefined) + return EventEmitter.defaultMaxListeners; + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return _getMaxListeners(this); +}; + +EventEmitter.prototype.emit = function emit(type) { + var args = []; + for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); + var doError = (type === 'error'); + + var events = this._events; + if (events !== undefined) + doError = (doError && events.error === undefined); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + var er; + if (args.length > 0) + er = args[0]; + if (er instanceof Error) { + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. + throw er; // Unhandled 'error' event + } + // At least give some kind of context to the user + var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); + err.context = er; + throw err; // Unhandled 'error' event + } + + var handler = events[type]; + + if (handler === undefined) + return false; + + if (typeof handler === 'function') { + ReflectApply(handler, this, args); + } else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + ReflectApply(listeners[i], this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + checkListener(listener); + + events = target._events; + if (events === undefined) { + events = target._events = Object.create(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener !== undefined) { + target.emit('newListener', type, + listener.listener ? listener.listener : listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (existing === undefined) { + // Optimize the case of one listener. Don't need the extra array object. + existing = events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === 'function') { + // Adding the second element, need to change to array. + existing = events[type] = + prepend ? [listener, existing] : [existing, listener]; + // If we've already got an array, just append. + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + + // Check for listener leak + m = _getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' ' + String(type) + ' listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + ProcessEmitWarning(w); + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = + function prependListener(type, listener) { + return _addListener(this, type, listener, true); + }; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) + return this.listener.call(this.target); + return this.listener.apply(this.target, arguments); + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + checkListener(listener); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + checkListener(listener); + this.prependListener(type, _onceWrap(this, type, listener)); + return this; + }; + +// Emits a 'removeListener' event if and only if the listener was removed. +EventEmitter.prototype.removeListener = + function removeListener(type, listener) { + var list, events, position, i, originalListener; + + checkListener(listener); + + events = this._events; + if (events === undefined) + return this; + + list = events[type]; + if (list === undefined) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else { + delete events[type]; + if (events.removeListener) + this.emit('removeListener', type, list.listener || listener); + } + } else if (typeof list !== 'function') { + position = -1; + + for (i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + originalListener = list[i].listener; + position = i; + break; + } + } + + if (position < 0) + return this; + + if (position === 0) + list.shift(); + else { + spliceOne(list, position); + } + + if (list.length === 1) + events[type] = list[0]; + + if (events.removeListener !== undefined) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (events === undefined) + return this; + + // not listening for removeListener, no need to emit + if (events.removeListener === undefined) { + if (arguments.length === 0) { + this._events = Object.create(null); + this._eventsCount = 0; + } else if (events[type] !== undefined) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = Object.keys(events); + var key; + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = Object.create(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners !== undefined) { + // LIFO order + for (i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; + }; + +function _listeners(target, type, unwrap) { + var events = target._events; + + if (events === undefined) + return []; + + var evlistener = events[type]; + if (evlistener === undefined) + return []; + + if (typeof evlistener === 'function') + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + + return unwrap ? + unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); +} + +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +EventEmitter.listenerCount = function(emitter, type) { + if (typeof emitter.listenerCount === 'function') { + return emitter.listenerCount(type); + } else { + return listenerCount.call(emitter, type); + } +}; + +EventEmitter.prototype.listenerCount = listenerCount; +function listenerCount(type) { + var events = this._events; + + if (events !== undefined) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener !== undefined) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; +}; + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +function spliceOne(list, index) { + for (; index + 1 < list.length; index++) + list[index] = list[index + 1]; + list.pop(); +} + +function unwrapListeners(arr) { + var ret = new Array(arr.length); + for (var i = 0; i < ret.length; ++i) { + ret[i] = arr[i].listener || arr[i]; + } + return ret; +} + +function once(emitter, name) { + return new Promise(function (resolve, reject) { + function errorListener(err) { + emitter.removeListener(name, resolver); + reject(err); + } + + function resolver() { + if (typeof emitter.removeListener === 'function') { + emitter.removeListener('error', errorListener); + } + resolve([].slice.call(arguments)); + }; + + eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); + if (name !== 'error') { + addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); + } + }); +} + +function addErrorHandlerIfEventEmitter(emitter, handler, flags) { + if (typeof emitter.on === 'function') { + eventTargetAgnosticAddListener(emitter, 'error', handler, flags); + } +} + +function eventTargetAgnosticAddListener(emitter, name, listener, flags) { + if (typeof emitter.on === 'function') { + if (flags.once) { + emitter.once(name, listener); + } else { + emitter.on(name, listener); + } + } else if (typeof emitter.addEventListener === 'function') { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen for `error` events here. + emitter.addEventListener(name, function wrapListener(arg) { + // IE does not have builtin `{ once: true }` support so we + // have to do it manually. + if (flags.once) { + emitter.removeEventListener(name, wrapListener); + } + listener(arg); + }); + } else { + throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); + } +} + +},{}],29:[function(require,module,exports){ +/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],30:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],31:[function(require,module,exports){ +(function (setImmediate,clearImmediate){(function (){ +var nextTick = require('process/browser.js').nextTick; +var apply = Function.prototype.apply; +var slice = Array.prototype.slice; +var immediateIds = {}; +var nextImmediateId = 0; + +// DOM APIs, for completeness + +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); +}; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); +}; +exports.clearTimeout = +exports.clearInterval = function(timeout) { timeout.close(); }; + +function Timeout(id, clearFn) { + this._id = id; + this._clearFn = clearFn; +} +Timeout.prototype.unref = Timeout.prototype.ref = function() {}; +Timeout.prototype.close = function() { + this._clearFn.call(window, this._id); +}; + +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; + +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; + +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); + + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); + } +}; + +// That's not how node.js implements it but the exposed api is the same. +exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { + var id = nextImmediateId++; + var args = arguments.length < 2 ? false : slice.call(arguments, 1); + + immediateIds[id] = true; + + nextTick(function onNextTick() { + if (immediateIds[id]) { + // fn.call() is faster so we optimize for the common use-case + // @see http://jsperf.com/call-apply-segu + if (args) { + fn.apply(null, args); + } else { + fn.call(null); + } + // Prevent ids from leaking + exports.clearImmediate(id); + } + }); + + return id; +}; + +exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { + delete immediateIds[id]; +}; +}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate) +},{"process/browser.js":30,"timers":31}]},{},[5])(5) +}); diff --git a/scripts/Ed25519 license.txt b/scripts/Ed25519 license.txt new file mode 100755 index 0000000..6d7451c --- /dev/null +++ b/scripts/Ed25519 license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Nicolas Flamel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/Ed25519-0.0.22.js b/scripts/Ed25519-0.0.22.js new file mode 100755 index 0000000..a592b8a --- /dev/null +++ b/scripts/Ed25519-0.0.22.js @@ -0,0 +1,292 @@ + +var ed25519 = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + + return ( +function(ed25519) { + ed25519 = ed25519 || {}; + + +var a;a||(a=typeof ed25519 !== 'undefined' ? ed25519 : {});var g,h;a.ready=new Promise(function(b,d){g=b;h=d});var k=Object.assign({},a),m="";"undefined"!=typeof document&&document.currentScript&&(m=document.currentScript.src);_scriptDir&&(m=_scriptDir);0!==m.indexOf("blob:")?m=m.substr(0,m.replace(/[?#].*/,"").lastIndexOf("/")+1):m="";var n=a.printErr||console.warn.bind(console);Object.assign(a,k);k=null;var p;a.wasmBinary&&(p=a.wasmBinary);var noExitRuntime=a.noExitRuntime||!0; +"object"!=typeof WebAssembly&&q("no native wasm support detected");var r,t=!1,u,v;function w(){var b=r.buffer;u=b;a.HEAP8=new Int8Array(b);a.HEAP16=new Int16Array(b);a.HEAP32=new Int32Array(b);a.HEAPU8=v=new Uint8Array(b);a.HEAPU16=new Uint16Array(b);a.HEAPU32=new Uint32Array(b);a.HEAPF32=new Float32Array(b);a.HEAPF64=new Float64Array(b)}var x=[],y=[],z=[];function A(){var b=a.preRun.shift();x.unshift(b)}var B=0,C=null,D=null; +function q(b){if(a.onAbort)a.onAbort(b);b="Aborted("+b+")";n(b);t=!0;b=new WebAssembly.RuntimeError(b+". Build with -sASSERTIONS for more info.");h(b);throw b;}function E(){return F.startsWith("data:application/octet-stream;base64,")}var F;F="." + getResource("./scripts/Ed25519-0.0.22.wasm");if(!E()){var G=F;F=a.locateFile?a.locateFile(G,m):m+G}function H(){var b=F;try{if(b==F&&p)return new Uint8Array(p);throw"both async and sync fetching of the wasm failed";}catch(d){q(d)}} +function I(){return p||"function"!=typeof fetch?Promise.resolve().then(function(){return H()}):fetch(F,{credentials:"same-origin"}).then(function(b){if(!b.ok)throw"failed to load wasm binary file at '"+F+"'";return b.arrayBuffer()}).catch(function(){return H()})}function J(b){for(;0>>=0;if(2147483648=l;l*=2){var f=d*(1+.2/l);f=Math.min(f,b+100663296);var c=Math;f=Math.max(b,f);c=c.min.call(c,2147483648,f+(65536-f%65536)%65536);a:{try{r.grow(c-u.byteLength+65535>>>16);w();var e=1;break a}catch(O){}e=void 0}if(e)return!0}return!1}}; +(function(){function b(c){a.asm=c.exports;r=a.asm.c;w();y.unshift(a.asm.d);B--;a.monitorRunDependencies&&a.monitorRunDependencies(B);0==B&&(null!==C&&(clearInterval(C),C=null),D&&(c=D,D=null,c()))}function d(c){b(c.instance)}function l(c){return I().then(function(e){return WebAssembly.instantiate(e,f)}).then(function(e){return e}).then(c,function(e){n("failed to asynchronously prepare wasm: "+e);q(e)})}var f={a:K};B++;a.monitorRunDependencies&&a.monitorRunDependencies(B);if(a.instantiateWasm)try{return a.instantiateWasm(f, +b)}catch(c){return n("Module.instantiateWasm callback failed with error: "+c),!1}(function(){return p||"function"!=typeof WebAssembly.instantiateStreaming||E()||"function"!=typeof fetch?l(d):fetch(F,{credentials:"same-origin"}).then(function(c){return WebAssembly.instantiateStreaming(c,f).then(d,function(e){n("wasm streaming compile failed: "+e);n("falling back to ArrayBuffer instantiation");return l(d)})})})().catch(h);return{}})(); +a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.d).apply(null,arguments)};a._publicKeySize=function(){return(a._publicKeySize=a.asm.e).apply(null,arguments)};a._publicKeyFromSecretKey=function(){return(a._publicKeyFromSecretKey=a.asm.f).apply(null,arguments)};a._signatureSize=function(){return(a._signatureSize=a.asm.g).apply(null,arguments)};a._sign=function(){return(a._sign=a.asm.h).apply(null,arguments)};a._verify=function(){return(a._verify=a.asm.i).apply(null,arguments)}; +a._malloc=function(){return(a._malloc=a.asm.j).apply(null,arguments)};a._free=function(){return(a._free=a.asm.k).apply(null,arguments)};var L;D=function M(){L||N();L||(D=M)}; +function N(){function b(){if(!L&&(L=!0,a.calledRun=!0,!t)){J(y);g(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;){var d=a.postRun.shift();z.unshift(d)}J(z)}}if(!(0 { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ 332: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_MODULE__332__; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "stringify": () => (/* binding */ stringify), +/* harmony export */ "parse": () => (/* binding */ parse), +/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) +/* harmony export */ }); +/* harmony import */ var bignumber_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(332); +/* harmony import */ var bignumber_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(bignumber_js__WEBPACK_IMPORTED_MODULE_0__); +// JSONBigNumber.js +// 2017-02-14 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// See http://www.JSON.org/js.html +// This code should be minified before deployment. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSONBigNumber object containing two methods: stringify +// and parse. + +// JSONBigNumber.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSONBigNumber.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSONBigNumber.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSONBigNumber.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSONBigNumber.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSONBigNumber.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSONBigNumber.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +// +a[5], +a[6])); +// } +// } +// return value; +// }); + +// myData = JSONBigNumber.parse('["Date(09/09/2001)"]', function (key, value) { +// var d; +// if (typeof value === "string" && +// value.slice(0, 5) === "Date(" && +// value.slice(-1) === ")") { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// }); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSONBigNumber, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + + + +var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + +function f(n) { + // Format integers to have at least two digits. + return n < 10 ? + "0" + n : + n; +} + +function this_value() { + return this.valueOf(); +} + +if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + "-" + + f(this.getUTCMonth() + 1) + "-" + + f(this.getUTCDate()) + "T" + + f(this.getUTCHours()) + ":" + + f(this.getUTCMinutes()) + ":" + + f(this.getUTCSeconds()) + "Z" : + null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; +} + +var gap; +var indent; +var meta; +var rep; + + +function quote(string) { + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) ? + "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" ? + c : + "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" : + "\"" + string + "\""; +} + + +function str(key, holder) { + + // Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + var isBigNumber = value != null && (value instanceof (bignumber_js__WEBPACK_IMPORTED_MODULE_0___default()) || bignumber_js__WEBPACK_IMPORTED_MODULE_0___default().isBigNumber(value)); + + // Check for NaN and Infinity + + if (isBigNumber && !value.isFinite()) { + value = null; + } + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === "object" && + typeof value.toJSON === "function") { + value = value.toJSON(key); + } + + // If we were called with a replacer function, then call the replacer to + // obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + + // What happens next depends on the value's type. + + switch (typeof value) { + case "string": + if (isBigNumber) { + return value; + } else { + return quote(value); + } + + case "number": + + // JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? + String(value) : + "null"; + + case "boolean": + case "null": + + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce "null". The case is included here in + // the remote chance that this gets fixed someday. + + return String(value); + + // If the type is "object", we might be dealing with an object or an array or + // null. + + case "object": + + // Due to a specification blunder in ECMAScript, typeof null is "object", + // so watch out for that case. + + if (!value) { + return "null"; + } + + // Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + + // Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + + v = partial.length === 0 ? + "[]" : + gap ? + "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : + "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + + // If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + gap ? + ": " : + ":" + ) + v); + } + } + } + } else { + + // Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + gap ? + ": " : + ":" + ) + v); + } + } + } + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + + v = partial.length === 0 ? + "{}" : + gap ? + "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : + "{" + partial.join(",") + "}"; + gap = mind; + return v; + } +} + +// If the JSON object does not yet have a stringify method, give it one. + +meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" +}; + +function stringify(value, replacer, space) { + + // The stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // A default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + + // If the space parameter is a number, make an indent string containing that + // many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + + // If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + + // If there is a replacer, it must be a function or an array. + // Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && + (typeof replacer !== "object" || + typeof replacer.length !== "number")) { + throw new Error("JSON.stringify"); + } + + // Make a fake root object containing our value under the key of "". + // Return the result of stringifying the value. + + return str("", { + "": value + }); +} + +// We are defining the function inside of another function to avoid creating +// global variables. + +var at; // The index of the current character +var ch; // The current character +var escapee = { + "\"": "\"", + "\\": "\\", + "/": "/", + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t" +}; +var text; + +var error = function (m) { + + // Call error when something is wrong. + + throw { + name: "SyntaxError", + message: m, + at: at, + text: text + }; +}; + +var next = function (c) { + + // If a c parameter is provided, verify that it matches the current character. + + if (c && c !== ch) { + error("Expected '" + c + "' instead of '" + ch + "'"); + } + + // Get the next character. When there are no more characters, + // return the empty string. + + ch = text.charAt(at); + at += 1; + return ch; +}; + +var number = function () { + + // Parse a number value to a BigNumber. + + var number; + var string = ""; + + if (ch === "-") { + string = "-"; + next("-"); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + if (ch === ".") { + string += "."; + while (next() && ch >= "0" && ch <= "9") { + string += ch; + } + } + if (ch === "e" || ch === "E") { + string += ch; + next(); + if (ch === "-" || ch === "+") { + string += ch; + next(); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + } + number = new (bignumber_js__WEBPACK_IMPORTED_MODULE_0___default())(string); + if (!number.isFinite()) { + error("Bad number"); + } else { + return number; + } +}; + +var string = function () { + + // Parse a string value. + + var hex; + var i; + var value = ""; + var uffff; + + // When parsing for string values, we must look for " and \ characters. + + if (ch === "\"") { + while (next()) { + if (ch === "\"") { + next(); + return value; + } + if (ch === "\\") { + next(); + if (ch === "u") { + uffff = 0; + for (i = 0; i < 4; i += 1) { + hex = parseInt(next(), 16); + if (!isFinite(hex)) { + break; + } + uffff = uffff * 16 + hex; + } + value += String.fromCharCode(uffff); + } else if (typeof escapee[ch] === "string") { + value += escapee[ch]; + } else { + break; + } + } else { + value += ch; + } + } + } + error("Bad string"); +}; + +var white = function () { + + // Skip whitespace. + + while (ch && ch <= " ") { + next(); + } +}; + +var word = function () { + + // true, false, or null. + + switch (ch) { + case "t": + next("t"); + next("r"); + next("u"); + next("e"); + return true; + case "f": + next("f"); + next("a"); + next("l"); + next("s"); + next("e"); + return false; + case "n": + next("n"); + next("u"); + next("l"); + next("l"); + return null; + } + error("Unexpected '" + ch + "'"); +}; + +var value; // Place holder for the value function. + +var array = function () { + + // Parse an array value. + + var arr = []; + + if (ch === "[") { + next("["); + white(); + if (ch === "]") { + next("]"); + return arr; // empty array + } + while (ch) { + arr.push(value()); + white(); + if (ch === "]") { + next("]"); + return arr; + } + next(","); + white(); + } + } + error("Bad array"); +}; + +var object = function () { + + // Parse an object value. + + var key; + var obj = {}; + + if (ch === "{") { + next("{"); + white(); + if (ch === "}") { + next("}"); + return obj; // empty object + } + while (ch) { + key = string(); + white(); + next(":"); + if (Object.hasOwnProperty.call(obj, key)) { + error("Duplicate key '" + key + "'"); + } + obj[key] = value(); + white(); + if (ch === "}") { + next("}"); + return obj; + } + next(","); + white(); + } + } + error("Bad object"); +}; + +value = function () { + + // Parse a JSON value. It could be an object, an array, a string, a number, + // or a word. + + white(); + switch (ch) { + case "{": + return object(); + case "[": + return array(); + case "\"": + return string(); + case "-": + return number(); + default: + return (ch >= "0" && ch <= "9") ? + number() : + word(); + } +}; + +// Set the New JSONBigNumber.parse function It will have access to all of the above +// functions and variables. + +function parse(source, reviver) { + var result; + + text = source; + at = 0; + ch = " "; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + + // If there is a reviver function, we recursively walk the new structure, + // passing each name/value pair to the reviver function for possible + // transformation, starting with a temporary root object that holds the result + // in an empty key. If there is not a reviver function, we simply return the + // result. + + return (typeof reviver === "function") ? (function walk(holder, key) { + var k; + var v; + var val = holder[key]; + if (val && typeof val === "object") { + for (k in val) { + if (Object.prototype.hasOwnProperty.call(val, k)) { + v = walk(val, k); + if (v !== undefined) { + val[k] = v; + } else { + delete val[k]; + } + } + } + } + return reviver.call(holder, key, val); + }({ + "": result + }, "")) : + result; +} + +/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ + parse: parse, + stringify: stringify +}); +})(); + +/******/ return __webpack_exports__; +/******/ })() +; +}); \ No newline at end of file diff --git a/scripts/SMAZ license.txt b/scripts/SMAZ license.txt new file mode 100755 index 0000000..28af572 --- /dev/null +++ b/scripts/SMAZ license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2023 Nicolas Flamel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/SMAZ-0.0.31.js b/scripts/SMAZ-0.0.31.js new file mode 100755 index 0000000..7e4afc1 --- /dev/null +++ b/scripts/SMAZ-0.0.31.js @@ -0,0 +1,261 @@ + +var smaz = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + + return ( +function(smaz) { + smaz = smaz || {}; + + +var a;a||(a=typeof smaz !== 'undefined' ? smaz : {});var g,h;a.ready=new Promise(function(b,d){g=b;h=d});var k=Object.assign({},a),m="";"undefined"!=typeof document&&document.currentScript&&(m=document.currentScript.src);_scriptDir&&(m=_scriptDir);0!==m.indexOf("blob:")?m=m.substr(0,m.replace(/[?#].*/,"").lastIndexOf("/")+1):m="";var n=a.printErr||console.warn.bind(console);Object.assign(a,k);k=null;var p;a.wasmBinary&&(p=a.wasmBinary);var noExitRuntime=a.noExitRuntime||!0; +"object"!=typeof WebAssembly&&q("no native wasm support detected");var r,t=!1,u,v;function w(){var b=r.buffer;u=b;a.HEAP8=new Int8Array(b);a.HEAP16=new Int16Array(b);a.HEAP32=new Int32Array(b);a.HEAPU8=v=new Uint8Array(b);a.HEAPU16=new Uint16Array(b);a.HEAPU32=new Uint32Array(b);a.HEAPF32=new Float32Array(b);a.HEAPF64=new Float64Array(b)}var x=[],y=[],z=[];function A(){var b=a.preRun.shift();x.unshift(b)}var B=0,C=null,D=null; +function q(b){if(a.onAbort)a.onAbort(b);b="Aborted("+b+")";n(b);t=!0;b=new WebAssembly.RuntimeError(b+". Build with -sASSERTIONS for more info.");h(b);throw b;}function E(){return F.startsWith("data:application/octet-stream;base64,")}var F;F="." + getResource("./scripts/SMAZ-0.0.31.wasm");if(!E()){var G=F;F=a.locateFile?a.locateFile(G,m):m+G}function H(){var b=F;try{if(b==F&&p)return new Uint8Array(p);throw"both async and sync fetching of the wasm failed";}catch(d){q(d)}} +function I(){return p||"function"!=typeof fetch?Promise.resolve().then(function(){return H()}):fetch(F,{credentials:"same-origin"}).then(function(b){if(!b.ok)throw"failed to load wasm binary file at '"+F+"'";return b.arrayBuffer()}).catch(function(){return H()})}function J(b){for(;0>>=0;if(2147483648=l;l*=2){var f=d*(1+.2/l);f=Math.min(f,b+100663296);var c=Math;f=Math.max(b,f);c=c.min.call(c,2147483648,f+(65536-f%65536)%65536);a:{try{r.grow(c-u.byteLength+65535>>>16);w();var e=1;break a}catch(O){}e=void 0}if(e)return!0}return!1}}; +(function(){function b(c){a.asm=c.exports;r=a.asm.c;w();y.unshift(a.asm.d);B--;a.monitorRunDependencies&&a.monitorRunDependencies(B);0==B&&(null!==C&&(clearInterval(C),C=null),D&&(c=D,D=null,c()))}function d(c){b(c.instance)}function l(c){return I().then(function(e){return WebAssembly.instantiate(e,f)}).then(function(e){return e}).then(c,function(e){n("failed to asynchronously prepare wasm: "+e);q(e)})}var f={a:K};B++;a.monitorRunDependencies&&a.monitorRunDependencies(B);if(a.instantiateWasm)try{return a.instantiateWasm(f, +b)}catch(c){return n("Module.instantiateWasm callback failed with error: "+c),!1}(function(){return p||"function"!=typeof WebAssembly.instantiateStreaming||E()||"function"!=typeof fetch?l(d):fetch(F,{credentials:"same-origin"}).then(function(c){return WebAssembly.instantiateStreaming(c,f).then(d,function(e){n("wasm streaming compile failed: "+e);n("falling back to ArrayBuffer instantiation");return l(d)})})})().catch(h);return{}})(); +a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.d).apply(null,arguments)};a._invalidSize=function(){return(a._invalidSize=a.asm.e).apply(null,arguments)};a._compressSize=function(){return(a._compressSize=a.asm.f).apply(null,arguments)};a._compress=function(){return(a._compress=a.asm.g).apply(null,arguments)};a._decompressSize=function(){return(a._decompressSize=a.asm.h).apply(null,arguments)};a._decompress=function(){return(a._decompress=a.asm.i).apply(null,arguments)}; +a._malloc=function(){return(a._malloc=a.asm.j).apply(null,arguments)};a._free=function(){return(a._free=a.asm.k).apply(null,arguments)};var L;D=function M(){L||N();L||(D=M)}; +function N(){function b(){if(!L&&(L=!0,a.calledRun=!0,!t)){J(y);g(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;){var d=a.postRun.shift();z.unshift(d)}J(z)}}if(!(0 { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + + return ( +function(x25519) { + x25519 = x25519 || {}; + + +var a;a||(a=typeof x25519 !== 'undefined' ? x25519 : {});var g,h;a.ready=new Promise(function(b,d){g=b;h=d});var k=Object.assign({},a),m="";"undefined"!=typeof document&&document.currentScript&&(m=document.currentScript.src);_scriptDir&&(m=_scriptDir);0!==m.indexOf("blob:")?m=m.substr(0,m.replace(/[?#].*/,"").lastIndexOf("/")+1):m="";var n=a.printErr||console.warn.bind(console);Object.assign(a,k);k=null;var p;a.wasmBinary&&(p=a.wasmBinary);var noExitRuntime=a.noExitRuntime||!0; +"object"!=typeof WebAssembly&&q("no native wasm support detected");var r,t=!1,u,v;function w(){var b=r.buffer;u=b;a.HEAP8=new Int8Array(b);a.HEAP16=new Int16Array(b);a.HEAP32=new Int32Array(b);a.HEAPU8=v=new Uint8Array(b);a.HEAPU16=new Uint16Array(b);a.HEAPU32=new Uint32Array(b);a.HEAPF32=new Float32Array(b);a.HEAPF64=new Float64Array(b)}var x=[],y=[],z=[];function A(){var b=a.preRun.shift();x.unshift(b)}var B=0,C=null,D=null; +function q(b){if(a.onAbort)a.onAbort(b);b="Aborted("+b+")";n(b);t=!0;b=new WebAssembly.RuntimeError(b+". Build with -sASSERTIONS for more info.");h(b);throw b;}function E(){return F.startsWith("data:application/octet-stream;base64,")}var F;F="." + getResource("./scripts/X25519-0.0.23.wasm");if(!E()){var G=F;F=a.locateFile?a.locateFile(G,m):m+G}function H(){var b=F;try{if(b==F&&p)return new Uint8Array(p);throw"both async and sync fetching of the wasm failed";}catch(d){q(d)}} +function I(){return p||"function"!=typeof fetch?Promise.resolve().then(function(){return H()}):fetch(F,{credentials:"same-origin"}).then(function(b){if(!b.ok)throw"failed to load wasm binary file at '"+F+"'";return b.arrayBuffer()}).catch(function(){return H()})}function J(b){for(;0>>=0;if(2147483648=l;l*=2){var f=d*(1+.2/l);f=Math.min(f,b+100663296);var c=Math;f=Math.max(b,f);c=c.min.call(c,2147483648,f+(65536-f%65536)%65536);a:{try{r.grow(c-u.byteLength+65535>>>16);w();var e=1;break a}catch(O){}e=void 0}if(e)return!0}return!1}}; +(function(){function b(c){a.asm=c.exports;r=a.asm.c;w();y.unshift(a.asm.d);B--;a.monitorRunDependencies&&a.monitorRunDependencies(B);0==B&&(null!==C&&(clearInterval(C),C=null),D&&(c=D,D=null,c()))}function d(c){b(c.instance)}function l(c){return I().then(function(e){return WebAssembly.instantiate(e,f)}).then(function(e){return e}).then(c,function(e){n("failed to asynchronously prepare wasm: "+e);q(e)})}var f={a:K};B++;a.monitorRunDependencies&&a.monitorRunDependencies(B);if(a.instantiateWasm)try{return a.instantiateWasm(f, +b)}catch(c){return n("Module.instantiateWasm callback failed with error: "+c),!1}(function(){return p||"function"!=typeof WebAssembly.instantiateStreaming||E()||"function"!=typeof fetch?l(d):fetch(F,{credentials:"same-origin"}).then(function(c){return WebAssembly.instantiateStreaming(c,f).then(d,function(e){n("wasm streaming compile failed: "+e);n("falling back to ArrayBuffer instantiation");return l(d)})})})().catch(h);return{}})(); +a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.d).apply(null,arguments)};a._secretKeySize=function(){return(a._secretKeySize=a.asm.e).apply(null,arguments)};a._secretKeyFromEd25519SecretKey=function(){return(a._secretKeyFromEd25519SecretKey=a.asm.f).apply(null,arguments)};a._publicKeySize=function(){return(a._publicKeySize=a.asm.g).apply(null,arguments)};a._publicKeyFromEd25519PublicKey=function(){return(a._publicKeyFromEd25519PublicKey=a.asm.h).apply(null,arguments)}; +a._sharedSecretKeySize=function(){return(a._sharedSecretKeySize=a.asm.i).apply(null,arguments)};a._sharedSecretKeyFromSecretKeyAndPublicKey=function(){return(a._sharedSecretKeyFromSecretKeyAndPublicKey=a.asm.j).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.k).apply(null,arguments)};a._free=function(){return(a._free=a.asm.l).apply(null,arguments)};var L;D=function M(){L||N();L||(D=M)}; +function N(){function b(){if(!L&&(L=!0,a.calledRun=!0,!t)){J(y);g(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;){var d=a.postRun.shift();z.unshift(d)}J(z)}}if(!(0" + Common.htmlEncode(address) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak(), false, function() { + + // Hide loading + self.getApplication().hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that copy button isn't clicked + copyButton.removeClass("clicked"); + + // Hide message + self.getMessage().hide(); + } + }); + + // Catch errors + }).catch(function(error) { + + // Show message and allow showing messages + self.getMessage().show(Language.getDefaultTranslation('Copy Address Error'), Message.createText(Language.getDefaultTranslation('Copying the address to your clipboard failed.')), false, function() { + + // Hide loading + self.getApplication().hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that copy button isn't clicked + copyButton.removeClass("clicked"); + + // Hide message + self.getMessage().hide(); + } + }); + }); + + }, ("type" in event["originalEvent"] === true && event["originalEvent"]["type"] === "touchend") ? 0 : AboutSection.COPY_ADDRESS_TO_CLIPBOARD_DELAY_MILLISECONDS); + }); + } + + // Get name + getName() { + + // Return name + return AboutSection.NAME; + } + + // Reset + reset() { + + // Reset + super.reset(); + } + + // Name + static get NAME() { + + // Return name + return "About"; + } + + // Private + + // Initialize + initialize(state) { + + // Set base class initialize + var baseClassInitialize = super.initialize(state); + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return initializing base class + return baseClassInitialize.then(function() { + + // Resolve + resolve(); + + // Reject error + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Get initialize error header + getInitializeErrorHeader() { + + // Return initialize error header + return Language.getDefaultTranslation('About Error'); + } + + // Add version information + addVersionInformation() { + + // Get version information display + var versionInformationDisplay = this.getDisplay().find("div.versionInformation"); + + // Get version release timestamp + var versionReleaseTimestamp = Date.parse(VERSION_RELEASE_DATE) / Common.MILLISECONDS_IN_A_SECOND; + + // Add version number to version information display + versionInformationDisplay.append("

" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Version number:')) + Language.createTranslatableContainer("", Language.getDefaultTranslation('(?<=:) ')) + Language.createTranslatableContainer("", "%1$v", [VERSION_NUMBER], "contextMenu") + "

"); + + // Add release date to version information display + versionInformationDisplay.append("

" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Release date:')) + Language.createTranslatableContainer("", Language.getDefaultTranslation('(?<=:) ')) + Language.createTranslatableContainer("", "%1$d", [versionReleaseTimestamp.toFixed()], "contextMenu") + "

"); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set source code + var sourceCode = "https://github.com/NicolasFlamel1/MWC-Wallet-Browser-Extension"; + } + + // Otherwise check if loading from file + else if(location["protocol"] === Common.FILE_PROTOCOL) { + + // Set source code + var sourceCode = "https://github.com/NicolasFlamel1/MWC-Wallet-Standalone"; + } + + // Otherwise + else { + + // Set source code + var sourceCode = "https://github.com/NicolasFlamel1/mwcwallet.com"; + } + + // Add source code to version information display + versionInformationDisplay.append("

" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Source code:')) + Language.createTranslatableContainer("", Language.getDefaultTranslation('(?<=:) ')) + Language.createTranslatableContainer("", "%1$m", [ + [ + + // Text + sourceCode, + + // URL + sourceCode, + + // Is external + true, + + // Is blob + false + ] + ]) + "

"); + + // Add wallet type to version information display + versionInformationDisplay.append("

" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Wallet type:')) + Language.createTranslatableContainer("", Language.getDefaultTranslation('(?<=:) ')) + Language.createTranslatableContainer("", "%1$x", [Consensus.walletTypeToText(Consensus.getWalletType())], "contextMenu") + "

"); + + // Add network type to version information display + versionInformationDisplay.append("

" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Network type:')) + Language.createTranslatableContainer("", Language.getDefaultTranslation('(?<=:) ')) + Language.createTranslatableContainer("", "%1$x", [Consensus.networkTypeToText(Consensus.getNetworkType())], "contextMenu") + "

"); + } + + // Add donate information + addDonateInformation() { + + // Get donate display + var donateDisplay = this.getDisplay().find("div.donate"); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Language.getDefaultTranslation('Donations are greatly appreciated and help fund the development of this extension.'); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('Donations are greatly appreciated and help fund the development of this app.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('Donations are greatly appreciated and help fund the development of this site.'); + } + + // Add message to donate display + donateDisplay.prepend(Language.createTranslatableContainer("

", message)); + } + + // Update copyright + updateCopyright() { + + // Get current timestamp + var currentTimestamp = new Date(); + + // Get current year + var currentYear = currentTimestamp.getFullYear(); + + // Check if the current year is greater than the copyright year + if(currentYear > COPYRIGHT_YEAR) { + + // Get new copyright + var newCopyright = Language.createTranslatableContainer("

", Language.getDefaultTranslation('© %1$s–%2$s Nicolas Flamel.'), [COPYRIGHT_YEAR.toFixed(), currentYear.toFixed()], "copyright"); + } + + // Otherwise + else { + + // Get new copyright + var newCopyright = Language.createTranslatableContainer("

", Language.getDefaultTranslation('© %1$s Nicolas Flamel.'), [COPYRIGHT_YEAR.toFixed()], "copyright"); + } + + // Replace copyright with the new copyright + this.getDisplay().find("p.copyright").replaceWith(newCopyright); + + // Get next year timestamp + var nextYearTimestamp = new Date(currentYear + 1, Common.JANUARY_MONTH_INDEX); + + // Set self + var self = this; + + // Set timeout + setTimeout(function() { + + // Update copyright + self.updateCopyright(); + + }, Math.min(nextYearTimestamp - currentTimestamp, Common.INT32_MAX_VALUE)); + } + + // Add disclaimer + addDisclaimer() { + + // Get disclaimer display + var disclaimerDisplay = this.getDisplay().find("div.disclaimer"); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Language.getDefaultTranslation('This extension is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this extension or the use or other dealings in this extension.'); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('This app is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this app or the use or other dealings in this app.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('This site is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with this site or the use or other dealings in this site.'); + } + + // Add disclaimer message to disclaimer display + disclaimerDisplay.append(Language.createTranslatableContainer("

", message)); + } + + // Add translation contribution message + addTranslationContributionMessage() { + + // Get translation contributors display + var translationContributorsDisplay = this.getDisplay().find("div.translationContributors"); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Language.getDefaultTranslation('The following people created the translations for this extension. You can email %1$m if you\'re interested in translating this extension into another language.'); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('The following people created the translations for this app. You can email %1$m if you\'re interested in translating this app into another language.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('The following people created the translations for this site. You can email %1$m if you\'re interested in translating this site into another language.'); + } + + // Add translation contribution message to translation contributors display + translationContributorsDisplay.prepend(Language.createTranslatableContainer("

", message, [ + [ + // Text + "nicolasflamel@mwcwallet.com", + + // URL + "mailto:nicolasflamel@mwcwallet.com", + + // Is external + true, + + // Is blob + false + ] + ])); + } + + // Add attributions + addAttributions() { + + // Get attributions display + var attributionsDisplay = this.getDisplay().find("div.attributions"); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Language.getDefaultTranslation('This extension utilizes code and assets from the following sources.'); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('This app utilizes code and assets from the following sources.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('This site utilizes code and assets from the following sources.'); + } + + // Add attributions message to attributions display + attributionsDisplay.prepend(Language.createTranslatableContainer("

", message)); + + // Go through all attributions + for(var name in ATTRIBUTIONS) { + + if(ATTRIBUTIONS.hasOwnProperty(name) === true) { + + // Check if attribution has a license + if("License Type" in ATTRIBUTIONS[name] === true && "License Path" in ATTRIBUTIONS[name] === true) { + + // Add attribution to attributions display + attributionsDisplay.find("ul").append("

  • " + Language.createTranslatableContainer("", Language.getDefaultTranslation('%1$y: %2$m, License: %3$m'), [ + + // Name + name, + + [ + // Text + ATTRIBUTIONS[name]["URL"], + + // URL + ATTRIBUTIONS[name]["URL"], + + // Is external + true, + + // Is blob + false + ], + + [ + // Text + ATTRIBUTIONS[name]["License Type"], + + // URL + ATTRIBUTIONS[name]["License Path"], + + // Is external + true, + + // Is blob + false + ] + ], "contextMenu") + "
  • "); + } + + // Otherwise + else { + + // Add attribution to attributions display + attributionsDisplay.find("ul").append("
  • " + Language.createTranslatableContainer("", Language.getDefaultTranslation('%1$y: %2$m'), [ + + // Name + name, + + [ + // Text + ATTRIBUTIONS[name]["URL"], + + // URL + ATTRIBUTIONS[name]["URL"], + + // Is external + true, + + // Is blob + false + ] + ], "contextMenu") + "
  • "); + } + } + } + } + + // Copy address to clipboard delay milliseconds + static get COPY_ADDRESS_TO_CLIPBOARD_DELAY_MILLISECONDS() { + + // Return copy address to clipboard delay milliseconds + return 175; + } +} + + +// Main function + +// Set global object's about section +globalThis["AboutSection"] = AboutSection; diff --git a/scripts/account_section.js b/scripts/account_section.js new file mode 100755 index 0000000..91175c3 --- /dev/null +++ b/scripts/account_section.js @@ -0,0 +1,622 @@ +// Use strict +"use strict"; + + +// Classes + +// Account section class +class AccountSection extends Section { + + // Public + + // Constructor + constructor(display, sections, settings, message, focus, application, unlocked, automaticLock, scroll, wallets, node, wakeLock, transactions, prices, clipboard) { + + // Delegate constructor + super(display, sections, settings, message, focus, application, unlocked, automaticLock, scroll, wallets, node, wakeLock, transactions, prices, clipboard); + + // Set self + var self = this; + + // Change password button click event + this.getDisplay().find("button.changePassword").on("click", function() { + + // Get button + var button = $(this); + + // Prevent showing messages + self.getMessage().prevent(); + + // Save focus, blur, and get focused element + var focusedElement = self.getFocus().save(true); + + // Check if focused element is the button + if(focusedElement !== Focus.NO_FOCUS && focusedElement.is(button) === true) { + + // Set that button is clicked + button.addClass("clicked"); + } + + // Disable unlocked + self.getUnlocked().disable(); + + // Show loading + self.getApplication().showLoading(); + + // Set that button is loading + button.addClass("loading"); + + // Get current password + var currentPassword = self.getDisplay().find("input.currentPassword").val(); + + // Get new password + var newPassword = self.getDisplay().find("input.newPassword").val(); + + // Get confirm new password + var confirmNewPassword = self.getDisplay().find("input.confirmNewPassword").val(); + + // Show change password error + var showChangePasswordError = function(error) { + + // TODO Securely clear the currentPassword, newPassword, and confirmNewPassword + + // Show message and allow showing messages + self.getMessage().show(Language.getDefaultTranslation('Change Password Error'), Message.createText(error), false, function() { + + // Hide loading + self.getApplication().hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Set that button isn't loading + button.removeClass("loading"); + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.getFocus().restore(false); + + // Hide message + self.getMessage().hide(); + } + }); + }; + + // Check if current password is invalid + if(currentPassword["length"] === 0) { + + // Show change password error + showChangePasswordError(Language.getDefaultTranslation('Current password is empty.')); + } + + // Otherwise check if current password is incorrect + else if(self.getWallets().isPassword(currentPassword) === false) { + + // Show change password error + showChangePasswordError(Language.getDefaultTranslation('Current password is incorrect.')); + } + + // Otherwise check if new password is invalid + else if(newPassword["length"] === 0) { + + // Show change password error + showChangePasswordError(Language.getDefaultTranslation('New password is empty.')); + } + + // Otherwise check if confirm new password is invalid + else if(confirmNewPassword["length"] === 0) { + + // Show change password error + showChangePasswordError(Language.getDefaultTranslation('Confirm new password is empty.')); + } + + // Otherwise check if new passwords don't match match + else if(newPassword !== confirmNewPassword) { + + // Show change password error + showChangePasswordError(Language.getDefaultTranslation('New passwords don\'t match.')); + } + + // Otherwise + else { + + // Prevent automatic lock + self.getAutomaticLock().prevent(); + + // Set timeout + setTimeout(function() { + + // Change password + self.getWallets().changePassword(currentPassword, newPassword).then(function() { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Changed password.')); + + // TODO Securely clear the currentPassword, newPassword, and confirmNewPassword + + // Allow automatic lock + self.getAutomaticLock().allow(); + + // Check if automatic lock isn't locking + if(self.getAutomaticLock().isLocking() === false) { + + // Show message and allow showing messages + self.getMessage().show(Language.getDefaultTranslation('Password Changed'), Message.createText(Language.getDefaultTranslation('Your password was successfully changed.')), false, function() { + + // Hide loading + self.getApplication().hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Clear current password, new password, and confirm new password values + self.getDisplay().find("input.currentPassword, input.newPassword, input.confirmNewPassword").val("").closest("div").parent().closest("div").removeClass("error"); + + // Set that button isn't loading + button.removeClass("loading"); + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Delete focus + self.getFocus().delete(); + + // Hide message + self.getMessage().hide(); + } + + // Otherwise + else { + + // Clear current password, new password, and confirm new password values + self.getDisplay().find("input.currentPassword, input.newPassword, input.confirmNewPassword").val("").closest("div").parent().closest("div").removeClass("error"); + } + }); + } + + // Otherwise + else { + + // Clear current password, new password, and confirm new password values + self.getDisplay().find("input.currentPassword, input.newPassword, input.confirmNewPassword").val("").closest("div").parent().closest("div").removeClass("error"); + + // Update state and catch errors + self.updateState().catch(function(error) { + + }); + } + + // Catch errors + }).catch(function(error) { + + // Allow automatic lock + self.getAutomaticLock().allow(); + + // Check if automatic lock isn't locking + if(self.getAutomaticLock().isLocking() === false) { + + // Show change password error + showChangePasswordError(error); + } + }); + }, AccountSection.CHANGE_PASSWORD_DELAY_MILLISECONDS); + } + }); + + // Current password, new password, or confirm new password key down event + this.getDisplay().find("input.currentPassword, input.newPassword, input.confirmNewPassword").on("keydown", function(event) { + + // Check if enter was pressed + if(event["which"] === "\r".charCodeAt(0)) { + + // Get input + var input = $(this); + + // Get input's display + var display = input.closest("div").parent().closest("div"); + + // Check if display doesn't show an error + if(display.hasClass("error") === false) { + + // Trigger click on change password button + self.getDisplay().find("button.changePassword").trigger("click"); + } + } + + // Current password, new password, or confirm new password input event + }).on("input", function() { + + // Get input + var input = $(this); + + // Check if input has focus + if(input.is(":focus") === true) { + + // Get input's display + var display = input.closest("div").parent().closest("div"); + + // Get input's value + var value = input.val(); + + // Check if value doesn't exist + if(value["length"] === 0) { + + // Set that display shows an error + display.addClass("error"); + + // Return + return; + } + + // Check if input is current password + if(input.hasClass("currentPassword") === true) { + + // Check if value isn't the current password + if(self.getWallets().isPassword(value) === false) { + + // Set that display shows an error + display.addClass("error"); + + // Return + return; + } + } + + // Otherwise check if input is the new password + else if(input.hasClass("newPassword") === true) { + + // Check if value isn't the confirm new password + if(value === self.getDisplay().find("input.confirmNewPassword").val()) { + + // Set that confirm new password's display doesn't show an error + self.getDisplay().find("input.confirmNewPassword").closest("div").parent().closest("div").removeClass("error"); + } + + // Otherwise check if confirm new password exists + else if(self.getDisplay().find("input.confirmNewPassword").val()["length"] !== 0) { + + // Set that confirm new password's display doesn't shows an error + self.getDisplay().find("input.confirmNewPassword").closest("div").parent().closest("div").addClass("error"); + } + } + + // Otherwise check if input is the confirm new password + else if(input.hasClass("confirmNewPassword") === true) { + + // Check if value isn't the new password + if(value !== self.getDisplay().find("input.newPassword").val()) { + + // Set that display shows an error + display.addClass("error"); + + // Return + return; + } + } + + // Set that display doesn't shows an error + display.removeClass("error"); + } + + // Current password, new password, or confirm new password focus event + }).on("focus", function() { + + // Get input + var input = $(this); + + // Get input's display + var display = input.closest("div").parent().closest("div"); + + // Check if display isn't disabled + if(display.hasClass("disabled") === false) { + + // Trigger input event + input.trigger("input"); + } + + // Current password, new password, or confirm new password blur event + }).on("blur", function() { + + // Check if automatic lock isn't locking + if(self.getAutomaticLock().isLocking() === false) { + + // Get input + var input = $(this); + + // Get input's display + var display = input.closest("div").parent().closest("div"); + + // Check if value is empty + if(input.val()["length"] === 0) { + + // Set that display doesn't shows an error + display.removeClass("error"); + } + } + }); + + // Show click event + this.getDisplay().find("span.show").on("click", function(event) { + + // Get target + var target = $(this); + + // Get input + var input = target.siblings("input"); + + // Check if input isn't disabled + if(input.is(":disabled") === false) { + + // Save input selection + var savedSelectionStart = input.get(0)["selectionStart"]; + var savedSelectionEnd = input.get(0)["selectionEnd"]; + var savedSelectionDirection = input.get(0)["selectionDirection"]; + + // Check if concealing password + if(target.hasClass("conceal") === true) { + + // Set title + var title = Language.getDefaultTranslation('Show'); + + // Show reveal icon and set title + target.removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Change password input type + input.attr("type", "password"); + } + + // Otherwise + else { + + // Set title + var title = Language.getDefaultTranslation('Hide'); + + // Show conceal icon and set title + target.addClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Change password input type + input.attr("type", "text"); + } + + // Request animation frame + requestAnimationFrame(function() { + + // Save focus and don't blur + self.getFocus().save(false); + + // Restore input selection + input.get(0).setSelectionRange(savedSelectionStart, savedSelectionEnd, savedSelectionDirection); + + // Restore focus and blur if it doesn't exist + self.getFocus().restore(true); + }); + } + + // Show mouse down event + }).on("mousedown", function(event) { + + // Get target + var target = $(this); + + // Check if target's input has focus + if(target.siblings("input").is(":focus") === true) { + + // Prevent stealing focus + event.preventDefault(); + + // Trigger focus change event + target.trigger(Common.FOCUS_CHANGE_EVENT); + } + }); + + // Delete all wallets button click event + this.getDisplay().find("button.deleteAllWallets").on("click", function() { + + // Get button + var button = $(this); + + // Set that button is clicked + button.addClass("clicked"); + + // Show message + self.getMessage().show(Language.getDefaultTranslation('Delete All Wallets'), Message.createText(Language.getDefaultTranslation('Are you sure you want to delete all your wallets?')) + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.')) + "", false, function() { + + // Save focus and blur + self.getFocus().save(true); + + // Disable unlocked + self.getUnlocked().disable(); + + }, Language.getDefaultTranslation('No'), Language.getDefaultTranslation('Yes'), false, Message.VISIBLE_STATE_UNLOCKED, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if deleting all wallets + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Prevent showing messages + self.getMessage().prevent(); + + // Show loading + self.getApplication().showLoading(); + + // Set that message second button is loading + self.getMessage().setButtonLoading(Message.SECOND_BUTTON); + + // Prevent automatic lock + self.getAutomaticLock().prevent(); + + // Set timeout + setTimeout(function() { + + // Remove all wallets + self.getWallets().removeAllWallets().then(function() { + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Delete focus + self.getFocus().delete(); + + // Show create display + self.getApplication().showCreateDisplay(); + + // Catch errors + }).catch(function(error) { + + // Allow automatic lock + self.getAutomaticLock().allow(); + + // Check if automatic lock isn't locking + if(self.getAutomaticLock().isLocking() === false) { + + // Show message and allow showing messages + self.getMessage().show(Language.getDefaultTranslation('Delete All Wallets Error'), Message.createText(error), true, function() { + + // Hide loading + self.getApplication().hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.getFocus().restore(false); + + // Hide message + self.getMessage().hide(); + } + }); + } + }); + }, Application.DELETE_ALL_WALLETS_DELAY_MILLISECONDS); + } + + // Otherwise + else { + + // Enable unlocked + self.getUnlocked().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.getFocus().restore(false); + + // Hide message + self.getMessage().hide(); + } + } + }); + }); + } + + // Get name + getName() { + + // Return name + return AccountSection.NAME; + } + + // Reset + reset() { + + // Reset + super.reset(); + + // Clear change password's current password, new password, and confirm new password an set their type to password and make them not showing errors + this.getDisplay().find("input.currentPassword, input.newPassword, input.confirmNewPassword").val("").attr("type", "password").closest("div").parent().closest("div").removeClass("error"); + + // Set that buttons aren't clicked or loading + this.getDisplay().find("button").removeClass("clicked loading"); + } + + // Name + static get NAME() { + + // Return name + return "Account"; + } + + // Private + + // Initialize + initialize(state) { + + // Set base class initialize + var baseClassInitialize = super.initialize(state); + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return initializing base class + return baseClassInitialize.then(function() { + + // Set title + var title = Language.getDefaultTranslation('Show'); + + // Show current password show icon, set its title, and change password input type + self.getDisplay().find("input.currentPassword").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + + // Show new password show icon, set its title, and change password input type + self.getDisplay().find("input.newPassword").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + + // Show confirm new password show icon, set its title, and change password input type + self.getDisplay().find("input.confirmNewPassword").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + + // Resolve + resolve(); + + // Reject error + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Get initialize error header + getInitializeErrorHeader() { + + // Return initialize error header + return Language.getDefaultTranslation('Account Error'); + } + + // Change password delay milliseconds + static get CHANGE_PASSWORD_DELAY_MILLISECONDS() { + + // Return change password delay milliseconds + return 300; + } +} + + +// Main function + +// Set global object's account section +globalThis["AccountSection"] = AccountSection; diff --git a/scripts/api.js b/scripts/api.js new file mode 100755 index 0000000..7fa08f4 --- /dev/null +++ b/scripts/api.js @@ -0,0 +1,17297 @@ +// Use strict +"use strict"; + + +// Classes + +// API class +class Api { + + // Public + + // Constructor + constructor(torProxy, node, transactions, wallets, settings, message, application, prices) { + + // Set Tor proxy + this.torProxy = torProxy; + + // Set node + this.node = node; + + // Set transactions + this.transactions = transactions; + + // Set wallets + this.wallets = wallets; + + // Set settings + this.settings = settings; + + // Set message + this.message = message; + + // Set application + this.application = application; + + // Set prices + this.prices = prices; + + // Set enable mining API to setting's default value + this.enableMiningApi = Api.SETTINGS_ENABLE_MINING_API_DEFAULT_VALUE; + + // Set require payment proof to settings's default value + this.requirePaymentProof = Api.SETTINGS_REQUIRE_PAYMENT_PROOF_DEFAULT_VALUE; + + // Set automatically approve receiving payments to settings's default value + this.automaticallyApproveReceivingPayments = Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_DEFAULT_VALUE; + + // Set current slate send ID + this.currentSlateSendId = Api.NO_CURRENT_SLATE_SEND_ID; + + // Set self + var self = this; + + // Once database is initialized + Database.onceInitialized(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return creating settings + return Promise.all([ + + // Enable mining API setting + self.settings.createValue(Api.SETTINGS_ENABLE_MINING_API_NAME, Api.SETTINGS_ENABLE_MINING_API_DEFAULT_VALUE), + + // Require payment proof setting + self.settings.createValue(Api.SETTINGS_REQUIRE_PAYMENT_PROOF_NAME, Api.SETTINGS_REQUIRE_PAYMENT_PROOF_DEFAULT_VALUE), + + // Automatically approve receiving payments + self.settings.createValue(Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_NAME, Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_DEFAULT_VALUE) + + ]).then(function() { + + // Initialize settings + var settings = [ + + // Enable mining API setting + Api.SETTINGS_ENABLE_MINING_API_NAME, + + // Require payment proof setting + Api.SETTINGS_REQUIRE_PAYMENT_PROOF_NAME, + + // Automatically approve receiving payments + Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_NAME + ]; + + // Return getting settings' values + return Promise.all(settings.map(function(setting) { + + // Return getting setting's value + return self.settings.getValue(setting); + + })).then(function(settingValues) { + + // Set enable mining API to setting's value + self.enableMiningApi = settingValues[settings.indexOf(Api.SETTINGS_ENABLE_MINING_API_NAME)]; + + // Set require payment proof to setting's value + self.requirePaymentProof = settingValues[settings.indexOf(Api.SETTINGS_REQUIRE_PAYMENT_PROOF_NAME)]; + + // Set automatically approve receiving payments to setting's value + self.automaticallyApproveReceivingPayments = settingValues[settings.indexOf(Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_NAME)]; + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + }); + + // Settings change event + $(this.settings).on(Settings.CHANGE_EVENT, function(event, setting) { + + // Check what setting was changes + switch(setting[Settings.DATABASE_SETTING_NAME]) { + + // Enable mining API setting + case Api.SETTINGS_ENABLE_MINING_API_NAME: + + // Set enable mining API to setting's value + self.enableMiningApi = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + + // Require payment proof setting + case Api.SETTINGS_REQUIRE_PAYMENT_PROOF_NAME: + + // Set require payment proof to setting's value + self.requirePaymentProof = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + + // Automatically approve receiving payments + case Api.SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_NAME: + + // Set automatically approve receiving payments to setting's value + self.automaticallyApproveReceivingPayments = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + } + }); + } + + // Get response + getResponse(api, wallet, type, data, allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check API + switch(api) { + + // Foreign API URL + case Api.FOREIGN_API_URL: + + // Check if type is JSON + if(type === "application/json") { + + // Check if data is a buffer + if(data instanceof Uint8Array === true) { + + // Try + try { + + // Parse data as JSON + data = JSONBigNumber.parse((new TextDecoder("utf-8", {"fatal": true})).decode(data)); + } + + // Catch errors + catch(error) { + + // Reject unsupported media type response + reject(Listener.UNSUPPORTED_MEDIA_TYPE_RESPONSE); + + // Return + return; + } + } + + // Check if data is a valid JSON-RPC request + if(JsonRpc.isRequest(data) === true) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check data's method + switch(data["method"]) { + + // Check version + case Api.CHECK_VERSION_METHOD: + + // Check if parameters are provided + if(data["params"] === null || Array.isArray(data["params"]) === true) { + + // Check if the correct number of parameters are provided + if(data["params"] === null || data["params"]["length"] === Api.CHECK_VERSION_PARAMETERS_LENGTH) { + + // Resolve + resolve([ + + // JSON-RPC response + JsonRpc.createResponse({ + + // Ok + "Ok": { + + // Foreign API version + "foreign_api_version": Api.CURRENT_FOREIGN_API_VERSION, + + // Supported slate versions + "supported_slate_versions": Slate.SUPPORTED_VERSIONS + } + }, data), + + // Method + data["method"], + + // Additional data + Api.NO_ADDITIONAL_DATA + ]); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC invalid request error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_REQUEST_ERROR, data)); + } + + // Break + break; + + // Build coinbase + case Api.BUILD_COINBASE_METHOD: + + // Check if mining API is enabled + if(self.enableMiningApi === true) { + + // Check if parameters are provided + if(Object.isObject(data["params"]) === true) { + + // Check if block fees parameter is invalid + if("block_fees" in data["params"] === false || Object.isObject(data["params"]["block_fees"]) === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if fees parameter is invalid + else if("fees" in data["params"]["block_fees"] === false || (Common.isNumberString(data["params"]["block_fees"]["fees"]) === false && data["params"]["block_fees"]["fees"] instanceof BigNumber === false) || (new BigNumber(data["params"]["block_fees"]["fees"])).isInteger() === false || (new BigNumber(data["params"]["block_fees"]["fees"])).isNegative() === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if height parameter is invalid + else if("height" in data["params"]["block_fees"] === false || (Common.isNumberString(data["params"]["block_fees"]["height"]) === false && data["params"]["block_fees"]["height"] instanceof BigNumber === false) || (new BigNumber(data["params"]["block_fees"]["height"])).isInteger() === false || (new BigNumber(data["params"]["block_fees"]["height"])).isLessThan(Consensus.FIRST_BLOCK_HEIGHT) === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if key ID parameter is invalid + else if("key_id" in data["params"]["block_fees"] === false || (data["params"]["block_fees"]["key_id"] !== Common.JSON_NULL_VALUE && (Common.isHexString(data["params"]["block_fees"]["key_id"]) === false || Common.hexStringLength(data["params"]["block_fees"]["key_id"]) !== Identifier.LENGTH))) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Get fees from parameters + var fees = new BigNumber(data["params"]["block_fees"]["fees"]); + + // Get height from parameters + var height = new BigNumber(data["params"]["block_fees"]["height"]); + + // Get key ID from parameters + var keyId = data["params"]["block_fees"]["key_id"]; + + // Try + try { + + // Create identifier from key ID or set it to no identifier it not available + var identifier = (keyId !== Common.JSON_NULL_VALUE) ? new Identifier(keyId) : Identifier.NO_IDENTIFIER; + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + + // Get coinbase + var getCoinbase = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Build coinbase + var buildCoinbase = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get using provided identifier + var usingProvidedIdentifier = identifier !== Identifier.NO_IDENTIFIER && wallet.getLastIdentifier().includesValue(identifier) === true; + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return wallet building coinbase + return wallet.buildCoinbase(fees, height, identifier, Wallet.NO_PROOF, HardwareWallet.NO_TEXT, [], allowUnlock, preventMessages, cancelOccurred).then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if using provided identifier + if(usingProvidedIdentifier === true) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Return getting a transaction with the coinbase's commit + return self.transactions.getTransaction(wallet.getWalletType(), wallet.getNetworkType(), coinbase[Wallet.COINBASE_COMMIT_INDEX]).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a transaction with the same commit doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Return building coinbase + return buildCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Return wallet getting coinbase proof + return wallet.getCoinbaseProof(fees, height, identifier, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue mining.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue mining.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return waiting for wallet's hardware wallet to approve + return self.wallets.waitForHardwareWalletToApprove(wallet.getKeyPath(), Message.createPendingResult() + Message.createLineBreak() + Message.createText((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue mining.') : Language.getDefaultTranslation('Approve receiving a transaction on the hardware wallet for %1$y to continue mining.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.'), [ + + // Account index + HardwareWallet.ACCOUNT.toFixed(), + + [ + + // Number + Consensus.getReward(wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, fees, height).dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + [ + + // Number + fees.dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + // Kernel features + Language.getDefaultTranslation('coinbase') + + ]), allowUnlock, preventMessages, cancelOccurred).then(function(canceled) { + + // Initialize hardware wallet disconnected + var hardwareWalletDisconnected = false; + + // Message replace API event + $(self.message).on(Message.REPLACE_EVENT + ".api", function(event, messageType, messageData) { + + // Check if message type is hardware wallet disconnect message + if(messageType === Application.HARDWARE_WALLET_DISCONNECT_MESSAGE) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Set hardware wallet disconnected + hardwareWalletDisconnected = true; + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + }); + + // Return wallet building coinbase + return wallet.buildCoinbase(fees, height, identifier, proof, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue mining.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue mining.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], true, true, function() { + + // Return if message was canceled + return canceled() === true; + + }).then(function(coinbase) { + + // Check if hardware wallet isn't disconnected + if(hardwareWalletDisconnected === false) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if using provided identifier + if(usingProvidedIdentifier === true) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Return getting a transaction with the coinbase's commit + return self.transactions.getTransaction(wallet.getWalletType(), wallet.getNetworkType(), coinbase[Wallet.COINBASE_COMMIT_INDEX]).then(function(transaction) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if a transaction with the same commit doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Return building coinbase + return buildCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Catch errors + }).catch(function(error) { + + // Check if hardware wallet isn't disconnected + if(hardwareWalletDisconnected === false) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + }); + } + + // Otherwise + else { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + }); + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return building a coinbase + return buildCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue mining.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue mining.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return building a coinbase + return buildCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve coinbase + resolve(coinbase); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting coinbase + return getCoinbase().then(function(coinbase) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Try + try { + + // Create kernel + var kernel = new SlateKernel(SlateKernel.COINBASE_FEATURES, new BigNumber(0), Slate.NO_LOCK_HEIGHT, Slate.NO_RELATIVE_HEIGHT, coinbase[Wallet.COINBASE_EXCESS_INDEX], coinbase[Wallet.COINBASE_EXCESS_SIGNATURE_INDEX]); + + // Create output + var output = new SlateOutput(SlateOutput.COINBASE_FEATURES, coinbase[Wallet.COINBASE_COMMIT_INDEX], coinbase[Wallet.COINBASE_PROOF_INDEX]); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Try + try { + + // Resolve + resolve([ + + // JSON-RPC response + JsonRpc.createResponse({ + + // Ok + "Ok": { + + // Key ID + "key_id": Common.toHexString(coinbase[Wallet.COINBASE_IDENTIFIER_INDEX].getValue()), + + // Output + "output": output.serialize(Api.COINBASE_SLATE_VERSION), + + // Kernel + "kernel": kernel.serialize(Api.COINBASE_SLATE_VERSION) + } + }, data), + + // Method + data["method"], + + // Additional data + Api.NO_ADDITIONAL_DATA + ]); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC invalid request error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_REQUEST_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC method not found error response + reject(JsonRpc.createErrorResponse(JsonRpc.METHOD_NOT_FOUND_ERROR, data)); + } + + // Break + break; + + // Receive transaction + case Api.RECEIVE_TRANSACTION_METHOD: + + // Check if parameters are provided + if(Array.isArray(data["params"]) === true) { + + // Check if the correct number of parameters are provided + if(data["params"]["length"] === Api.RECEIVE_TRANSACTION_PARAMETERS_LENGTH) { + + // Decode slate + var decodeSlate = function(serializedSlateSlatepackOrEmojiText) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a Slatepack or emoji text is provided + if(typeof serializedSlateSlatepackOrEmojiText === "string") { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Get Slatepack + var slatepack = serializedSlateSlatepackOrEmojiText; + + // Check if Slatepack is encrypted + if(Slatepack.isEncryptedSlatepack(slatepack) === true) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return getting wallet's Tor secret key + return wallet.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(secretKey) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack, secretKey).then(function(slate) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack, wallet.getHardwareWallet(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Get emoji text + var emojiText = serializedSlateSlatepackOrEmojiText; + + // Try + try { + + // Get slate from the emoji text + var slate = JSONBigNumber.parse(Emoji.decode(emojiText)); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + + // Check if slate is a serialized slate + if(Object.isObject(slate) === true) { + + // Resolve slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Break + break; + } + } + + // Otherwise check if a serialized slate is provided + else if(Object.isObject(serializedSlateSlatepackOrEmojiText) === true) { + + // Get slate + var slate = serializedSlateSlatepackOrEmojiText; + + // Resolve the slate + resolve(slate); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return decoding slate + return decodeSlate(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return parsing decoded slate as a slate + return Slate.parseSlateAsynchronous(decodedSlate, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, Slate.COMPACT_SLATE_PURPOSE_SEND_INITIAL).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if there's not the expected number of slate participants + if(slate.getNumberOfParticipants().isEqualTo(Api.RECEIVE_TRANSACTION_EXPECTED_NUMBER_OF_SLATE_PARTICIPANTS) === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Check if there's no room to add another participant to the slate + else if(slate.getNumberOfParticipants().isLessThanOrEqualTo(slate.getParticipants()["length"]) === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Check if slate's sender participant doesn't exist + else if(slate.getParticipant(SlateParticipant.SENDER_ID) === Slate.NO_PARTICIPANT) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if slate's sender participant is already complete + else if(slate.getParticipant(SlateParticipant.SENDER_ID).isComplete() === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if there's not the expected number of slate kernels + else if(slate.getKernels()["length"] !== Api.RECEIVE_TRANSACTION_EXPECTED_NUMBER_OF_SLATE_KERNELS) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if slate's kernel is already complete + else if(slate.getKernels()[0].isComplete() === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Check if slate already has a receiver signature + else if(slate.getReceiverSignature() !== Slate.NO_RECEIVER_SIGNATURE) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise check if payment proof is required and the slate doesn't have a payment proof + else if(self.requirePaymentProof === true && slate.hasPaymentProof() === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Check if a Slatepack is provided, the Slatepack is encrypted, and the slate contains a payment proof + if(typeof data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX] === "string" && Slatepack.isEncryptedSlatepack(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]) === true && slate.hasPaymentProof() === true) { + + // Check if slate's sender address length + switch(slate.getSenderAddress()["length"]) { + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Break + break; + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Check if sender address isn't for the Slatepack's sender public key + if(slate.getSenderAddress() !== Tor.publicKeyToTorAddress(Slatepack.getSlatepackSenderPublicKey(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]))) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + + // Break + break; + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + + // Check if slate's receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Break + break; + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Check if receiver address isn't for the Slatepack's receiver public key + if(slate.getReceiverAddress() !== Tor.publicKeyToTorAddress(Slatepack.getSlatepackReceiverPublicKey(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]))) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + + // Break + break; + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Return + return; + } + } + + // Get current height + var currentHeight = self.node.getCurrentHeight().getHeight(); + + // Check if slate's time to live cut off height exists and is expired + if(slate.getTimeToLiveCutOffHeight() !== Slate.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT && currentHeight !== Node.UNKNOWN_HEIGHT && currentHeight.isEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === false && slate.getTimeToLiveCutOffHeight().isLessThanOrEqualTo(currentHeight) === true) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Return getting a transaction for the wallet with the same ID + return self.transactions.getWalletsTransactionWithId(wallet.getKeyPath(), slate.getId()).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a transaction for the wallet with the same ID doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Get slate's offset + var offset = new Uint8Array(slate.getOffset()); + + // Is kernel offset unique + var isKernelOffsetUnique = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Create slate offset + slate.createOffset(); + } + + // Try + try { + + // Get slate's kernel offset + var kernelOffset = slate.getOffsetExcess(); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Return getting a received transaction for the wallet with the kernel offset + return self.transactions.getWalletsReceivedTransactionWithKernelOffset(wallet.getKeyPath(), kernelOffset).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a received transaction for the wallet with the same kernel offset doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve true + resolve(true); + } + + // Otherwise + else { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Return getting if the slate's kernel offset is unique + return isKernelOffsetUnique().then(function(result) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve result + resolve(result); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Resolve false + resolve(false); + } + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting if the slate's kernel offset is unique + return isKernelOffsetUnique().then(function(isUnique) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a the slate's kernel offset is unique + if(isUnique === true) { + + // Get proof address + var getProofAddress = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate has payment proof + if(slate.hasPaymentProof() === true) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Check if the slate's sender and receiver address are the same + if(slate.getReceiverAddress() === slate.getSenderAddress()) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Return getting Tor proof address + return wallet.getTorProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set slate's receiver address to the proof address + slate.setReceiverAddress(proofAddress); + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return getting Slatepack proof address + return wallet.getSlatepackProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set slate's receiver address to the proof address + slate.setReceiverAddress(proofAddress); + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Otherwise + else { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return getting Tor proof address + return wallet.getTorProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Return getting MQS proof address + return wallet.getMqsProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Slatepack address length + case Slatepack.ADDRESS_LENGTH: + + // Return getting Slatepack proof address + return wallet.getSlatepackProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return getting Tor proof address + return wallet.getTorProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + } + } + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if the slate's sender and receiver address are the same + if(slate.getReceiverAddress() === slate.getSenderAddress()) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Return getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set slate's receiver address to the proof address + slate.setReceiverAddress(proofAddress); + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return getting Slatepack proof address + return wallet.getSlatepackProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set slate's receiver address to the proof address + slate.setReceiverAddress(proofAddress); + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Return getting MQS proof address + return wallet.getMqsProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Slatepack address length + case Slatepack.ADDRESS_LENGTH: + + // Return getting Slatepack proof address + return wallet.getSlatepackProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + } + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate doesn't have a payment proof or its receiver address is the same as the proof address + if(slate.hasPaymentProof() === false || proofAddress === slate.getReceiverAddress()) { + + // Get output + var getOutput = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Build output + var buildOutput = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return wallet building output + return wallet.buildOutput(slate.getAmount(), Identifier.NO_HEIGHT, HardwareWallet.RECEIVING_TRANSACTION_MESSAGE, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return getting a transaction with the output's commit + return self.transactions.getTransaction(wallet.getWalletType(), wallet.getNetworkType(), output[Wallet.OUTPUT_COMMIT_INDEX]).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a transaction with the same commit doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Try + try { + + // Create a slate output from the output + var slateOutput = new SlateOutput(output[Wallet.OUTPUT_FEATURES_INDEX], output[Wallet.OUTPUT_COMMIT_INDEX], output[Wallet.OUTPUT_PROOF_INDEX]); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Return adding output to slate + return Slate.addOutputsAsynchronous(slate, [slateOutput]).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Add slate participant + var addSlateParticipant = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Get approval + var getApproval = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate was sent from self + if(self.currentSlateSendId !== Api.NO_CURRENT_SLATE_SEND_ID && Common.arraysAreEqualTimingSafe(slate.getId().getData(), self.currentSlateSendId.getData()) === true) { + + // Resolve + resolve(); + } + + // Otherwise check if automatically approving receiving payments + else if(self.automaticallyApproveReceivingPayments === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Show application approve receiving payment message + return self.application.showApproveReceivingPaymentMessage(wallet, slate, allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting approval + return getApproval().then(function() { + + // Get secret key + var getSecretKey = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return getting sum from wallet's sum + return wallet.getSum( + + // Outputs + [output], + + // Inputs + [] + + ).then(function(sum) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Return applying offset to slate + return slate.applyOffset(sum).then(function(offset) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Securely clear sum + sum.fill(0); + + // Resolve offset + resolve(offset); + } + + // Otherwise + else { + + // Securely clear sum and offset + sum.fill(0); + offset.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear sum + sum.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Resolve sum + resolve(sum); + } + } + + // Otherwise + else { + + // Securely clear sum + sum.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting secret key + return getSecretKey().then(function(secretKey) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if creating a secret nonce was successful + var secretNonce = Secp256k1Zkp.createSecretNonce(); + + if(secretNonce !== Secp256k1Zkp.OPERATION_FAILED) { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Check if combining the slate's offset with the offset failed + if(slate.combineOffsets(offset) === false) { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + } + + // Return adding participant to slate + return slate.addParticipant(secretKey, secretNonce, SlateParticipant.NO_MESSAGE, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Securely clear the secret key + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Securely clear the secret key + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return starting transaction for the output amount with the wallet's hardware wallet + return wallet.getHardwareWallet().startTransaction(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX, output[Wallet.OUTPUT_AMOUNT_INDEX], new BigNumber(0), slate.getFee(), HardwareWallet.NO_SECRET_NONCE_INDEX, slate.getSenderAddress(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return including output in the transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().includeOutputInTransaction(output[Wallet.OUTPUT_AMOUNT_INDEX], output[Wallet.OUTPUT_IDENTIFIER_INDEX], output[Wallet.OUTPUT_SWITCH_TYPE_INDEX], (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Apply offset + var applyOffset = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return applying offset to slate + return slate.applyOffset(wallet.getHardwareWallet(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject hardware wallet disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return applying offset + return applyOffset().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate was sent from self + if(self.currentSlateSendId !== Api.NO_CURRENT_SLATE_SEND_ID && Common.arraysAreEqualTimingSafe(slate.getId().getData(), self.currentSlateSendId.getData()) === true) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Save slate's offset + var oldOffset = new Uint8Array(slate.getOffset()); + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Check if combining the slate's offset with the offset failed + if(slate.combineOffsets(offset) === false) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Save slate's receiver signature + var oldReceiverSignature = slate.getReceiverSignature(); + + // Return adding participant to slate + return slate.addParticipant(wallet.getHardwareWallet(), Slate.NO_SECRET_NONCE, SlateParticipant.NO_MESSAGE, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return completing transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().completeTransaction().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Remove participant from slate + slate.getParticipants().pop(); + + // Restore slate's old receiver signature + slate.setReceiverSignature(oldReceiverSignature); + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to approve + return self.wallets.waitForHardwareWalletToApprove(wallet.getKeyPath(), Message.createPendingResult() + Message.createLineBreak() + Message.createText((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Approve receiving a transaction on the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Approve receiving a transaction on the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((slate.getSenderAddress() !== Slate.NO_SENDER_ADDRESS) ? Language.getDefaultTranslation('Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and the sender payment proof address displayed matches the following payment proof address.') : Language.getDefaultTranslation('Verify that the account index displayed on the hardware wallet is %1$s, the amount displayed is %2$c, the fee displayed is %3$c, the kernel features displayed is %4$x, and that there\'s no sender payment proof address displayed.'), [ + + // Account index + HardwareWallet.ACCOUNT.toFixed(), + + [ + + // Number + slate.getAmount().dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + [ + + // Number + slate.getFee().dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + // Kernel features + slate.getDisplayKernelFeatures() + + ]) + ((slate.getSenderAddress() !== Slate.NO_SENDER_ADDRESS) ? Message.createLineBreak() + Message.createLineBreak() + "" + Common.htmlEncode(slate.getSenderAddress()) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.')) + "" : (Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.')) + "")), allowUnlock, preventMessages, cancelOccurred).then(function(canceled) { + + // Initialize hardware wallet disconnected + var hardwareWalletDisconnected = false; + + // Message replace API event + $(self.message).on(Message.REPLACE_EVENT + ".api", function(event, messageType, messageData) { + + // Check if message type is hardware wallet disconnect message + if(messageType === Application.HARDWARE_WALLET_DISCONNECT_MESSAGE) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Set hardware wallet disconnected + hardwareWalletDisconnected = true; + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + }); + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Save slate's offset + var oldOffset = new Uint8Array(slate.getOffset()); + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Check if combining the slate's offset with the offset failed + if(slate.combineOffsets(offset) === false) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Save slate's receiver signature + var oldReceiverSignature = slate.getReceiverSignature(); + + // Return adding participant to slate + return slate.addParticipant(wallet.getHardwareWallet(), Slate.NO_SECRET_NONCE, SlateParticipant.NO_MESSAGE, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], true, true, function() { + + // Return if message was canceled + return canceled() === true; + + }).then(function() { + + // Check if hardware wallet isn't disconnected + if(hardwareWalletDisconnected === false) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return completing transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().completeTransaction().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Remove participant from slate + slate.getParticipants().pop(); + + // Restore slate's old receiver signature + slate.setReceiverSignature(oldReceiverSignature); + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Cancel transaction with the wallet's hardware wallet and catch errors + wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + }); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if hardware wallet isn't disconnected + if(hardwareWalletDisconnected === false) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + }); + } + + // Otherwise + else { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Restore slate's old offset + slate.offset = oldOffset; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Cancel transaction with the wallet's hardware wallet and catch errors + wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + }); + } + } + }); + } + + // Otherwise + else { + + // Check if hardware wallet isn't disconnected + if(hardwareWalletDisconnected === false) { + + // Turn off message replace API event + $(self.message).off(Message.REPLACE_EVENT + ".api"); + + // Get if message was canceled + var messageCanceled = canceled() === true; + + // Check if message wasn't canceled + if(messageCanceled === false) { + + // Disable message + self.message.disable(); + } + + // Set hide message + var hideMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if message was canceled + if(messageCanceled === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return setting that hardware wallet is done approving + return self.wallets.hardwareWalletDoneApproving(preventMessages).then(function() { + + // Resolve + resolve(); + }); + } + }); + }; + + // Return hiding message + return hideMessage().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur and message wasn't canceled + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && messageCanceled === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Encode slate + var encodeSlate = function(serializedSlate) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Check if serialized slate is binary + if(serializedSlate instanceof Uint8Array === true) { + + // Check if a Slatepack is provided and it's encrypted + if(typeof data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX] === "string" && Slatepack.isEncryptedSlatepack(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]) === true) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return getting wallet's Tor secret key + return wallet.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(secretKey) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate, secretKey, Slatepack.getSlatepackSenderPublicKey(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX])).then(function(slatepack) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate, wallet.getHardwareWallet(), Slatepack.getSlatepackSenderPublicKey(data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX]), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Otherwise + else { + + // Resolve the serialized slate + resolve(serializedSlate); + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check if a emoji text is provided + if(typeof data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX] === "string") { + + // Get emoji text from serialized slate + var emojiText = Emoji.encode(JSONBigNumber.stringify(serializedSlate)); + + // Resolve emoji text + resolve(emojiText); + } + + // Otherwise + else { + + // Resolve the serialized slate + resolve(serializedSlate); + } + + // Break + break; + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Check if slate has payment proof + if(slate.hasPaymentProof() === true) { + + // Try + try { + + // Get excess from slate + var excess = slate.getExcess(); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Get payment proof + var getPaymentProof = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return wallet building Tor payment proof + return wallet.buildTorPaymentProof(slate.getAmount(), excess, slate.getSenderAddress()).then(function(paymentProof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve payment proof + resolve(paymentProof); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Return wallet building MQS payment proof + return wallet.buildMqsPaymentProof(slate.getAmount(), excess, slate.getSenderAddress()).then(function(paymentProof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve payment proof + resolve(paymentProof); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Slatepack address length + case Slatepack.ADDRESS_LENGTH: + + // Return wallet building Slatepack payment proof + return wallet.buildSlatepackPaymentProof(slate.getAmount(), excess, slate.getSenderAddress()).then(function(paymentProof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve payment proof + resolve(paymentProof); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check receiver address length + switch(slate.getReceiverAddress()["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Return wallet building Tor payment proof + return wallet.buildTorPaymentProof(slate.getAmount(), excess, slate.getSenderAddress()).then(function(paymentProof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve payment proof + resolve(paymentProof); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // Default + default: + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + + // Break + break; + } + + // Break + break; + } + } + + // Otherwise + else { + + // Resolve payment proof + resolve(slate.getReceiverSignature()); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return getting payment proof + return getPaymentProof().then(function(paymentProof) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if setting slate's receiver signature to the payment proof was successful + if(slate.setReceiverSignature(paymentProof, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) === true) { + + // Try + try { + + // Serialize the slate + var serializedSlate = slate.serialize(wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, Slate.COMPACT_SLATE_PURPOSE_SEND_RESPONSE, typeof data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX] === "string"); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Return encoding slate + return encodeSlate(serializedSlate).then(function(encodedSlate) { + + // Resolve + resolve([ + + // JSON-RPC response + JsonRpc.createResponse({ + + // Ok + "Ok": encodedSlate + + }, data), + + // Method + data["method"], + + // Additional data + [ + + // Slate + slate, + + // Commit + output[Wallet.OUTPUT_COMMIT_INDEX], + + // Identifier + output[Wallet.OUTPUT_IDENTIFIER_INDEX], + + // Switch type + output[Wallet.OUTPUT_SWITCH_TYPE_INDEX] + ] + ]); + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Try + try { + + // Serialize the slate + var serializedSlate = slate.serialize(wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, Slate.COMPACT_SLATE_PURPOSE_SEND_RESPONSE, typeof data["params"][Api.RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX] === "string"); + } + + // Catch errors + catch(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + + // Return + return; + } + + // Return encoding slate + return encodeSlate(serializedSlate).then(function(encodedSlate) { + + // Resolve + resolve([ + + // JSON-RPC response + JsonRpc.createResponse({ + + // Ok + "Ok": encodedSlate + + }, data), + + // Method + data["method"], + + // Additional data + [ + + // Slate + slate, + + // Commit + output[Wallet.OUTPUT_COMMIT_INDEX], + + // Identifier + output[Wallet.OUTPUT_IDENTIFIER_INDEX], + + // Switch type + output[Wallet.OUTPUT_SWITCH_TYPE_INDEX] + ] + ]); + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC invalid request error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_REQUEST_ERROR, data)); + } + + // Break + break; + + // Get proof address + case Api.GET_PROOF_ADDRESS_METHOD: + + // Check if parameters are provided + if(Array.isArray(data["params"]) === true) { + + // Check if the correct number of parameters are provided + if(data["params"]["length"] === Api.GET_PROOF_ADDRESS_PARAMETERS_LENGTH) { + + // Get proof address + var getProofAddress = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return wallet getting Slatepack proof address + return wallet.getSlatepackProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + }); + } + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return wallet getting Slatepack proof address + return wallet.getSlatepackProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue receiving a payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue receiving a payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], allowUnlock, preventMessages, cancelOccurred).then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }); + } + + // Otherwise + else { + + // Return getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise check if canceled or user rejected on hardware wallet + else if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled, user rejected on hardware wallet, or hardware wallet is disconnected + if(error === Common.CANCELED_ERROR || error === HardwareWallet.USER_REJECTED_ERROR || error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + }; + + // Return wallet getting proof address + return getProofAddress().then(function(proofAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve([ + + // JSON-RPC response + JsonRpc.createResponse({ + + // Ok + "Ok": proofAddress + + }, data), + + // Method + data["method"], + + // Additional data + Api.NO_ADDITIONAL_DATA + ]); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + }); + } + + // Otherwise + else { + + // Reject JSON-RPC invalid parameters error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_PARAMETERS_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject JSON-RPC invalid request error response + reject(JsonRpc.createErrorResponse(JsonRpc.INVALID_REQUEST_ERROR, data)); + } + + // Break + break; + + // Default + default: + + // Reject JSON-RPC method not found error response + reject(JsonRpc.createErrorResponse(JsonRpc.METHOD_NOT_FOUND_ERROR, data)); + + // Break + break; + } + } + + // Otherwise + else { + + // Reject JSON-RPC internal error error response + reject(JsonRpc.createErrorResponse(JsonRpc.INTERNAL_ERROR_ERROR, data)); + } + } + + // Otherwise + else { + + // Reject unsupported media type response + reject(Listener.UNSUPPORTED_MEDIA_TYPE_RESPONSE); + } + } + + // Otherwise + else { + + // Reject unsupported media type response + reject(Listener.UNSUPPORTED_MEDIA_TYPE_RESPONSE); + } + + // Break + break; + + // Default + default: + + // Reject not found response + reject(Listener.NOT_FOUND_RESPONSE); + + // Break + break; + } + }); + } + + // Get fee + getFee(wallet, amount = Api.ALL_AMOUNT, baseFee = Api.DEFAULT_BASE_FEE, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if amount is invalid + if(amount !== Api.ALL_AMOUNT && amount.isLessThan(Slate.MINIMUM_AMOUNT) === true) { + + // Reject error + reject(Language.getDefaultTranslation('Amount is invalid.')); + } + + // Otherwise check if base fee is invalid + else if(baseFee.isLessThan(Api.MINIMUM_BASE_FEE) === true) { + + // Reject error + reject(Language.getDefaultTranslation('Base fee is invalid.')); + } + + // Otherwise + else { + + // Initialize total amount + var totalAmount = new BigNumber(0); + + // Initialize number of inputs + var numberOfInputs = 0; + + // Get fee, amount, and base fee + var getFeeAmountAndBaseFee = function(transactionIndex) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if amount is all amount + if(amount === Api.ALL_AMOUNT) { + + // Return get all wallet's received released transactions + return self.transactions.getWalletsReceivedReleasedTransactions(wallet.getKeyPath(), Database.GET_ALL_RESULTS, Database.GET_ALL_RESULTS).then(function(transactions) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Go through all transactions + for(var i = 0; i < transactions["length"]; ++i) { + + // Get transaction + var transaction = transactions[i]; + + // Update total amount + totalAmount = totalAmount.plus(transaction.getAmount()); + + // Increment number of inputs + ++numberOfInputs; + } + + // Get the fee + var fee = Slate.getRequiredFee(numberOfInputs, 1, 1, baseFee); + + // Update the total amount to not include the fee + totalAmount = totalAmount.minus(fee); + + // Check if the total amount is zero or less + if(totalAmount.isLessThanOrEqualTo(0) === true) { + + // Reject error + reject(Language.getDefaultTranslation('Insufficient balance.')); + } + + // Otherwise check if fee is invalid + else if(fee.isLessThan(Slate.MINIMUM_FEE) === true || fee.isGreaterThan(Slate.MAXIMUM_FEE) === true) { + + // Reject error + reject(Language.getDefaultTranslation('The fee is invalid.')); + } + + // Otherwise + else { + + // Resolve + resolve([ + + // Fee + fee, + + // Amount + totalAmount, + + // Base fee + baseFee + ]); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return get a group of the wallet's received released transactions + return self.transactions.getWalletsReceivedReleasedTransactions(wallet.getKeyPath(), transactionIndex, Api.SEND_TRANSACTIONS_GROUP_SIZE).then(function(transactions) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Go through all transactions or run at least once + for(var i = 0; i < transactions["length"] || i === 0; ++i) { + + // Check if a transaction exists + if(transactions["length"] !== 0) { + + // Get transaction + var transaction = transactions[i]; + + // Update total amount + totalAmount = totalAmount.plus(transaction.getAmount()); + + // Increment number of inputs + ++numberOfInputs; + } + + // Get the fee with one output + var fee = Slate.getRequiredFee(numberOfInputs, 1, 1, baseFee); + + // Get returned amount by subtracting the amount and fees from the total amount + var returnedAmount = totalAmount.minus(amount.plus(fee)); + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Get fee with two outputs + fee = Slate.getRequiredFee(numberOfInputs, 2, 1, baseFee); + } + + // Check if total amount is enough to cover the amount and fee + if((returnedAmount.isZero() === true && totalAmount.isEqualTo(amount.plus(fee)) === true) || (returnedAmount.isZero() === false && totalAmount.isGreaterThan(amount.plus(fee)) === true)) { + + // Check if fee is invalid + if(fee.isLessThan(Slate.MINIMUM_FEE) === true || fee.isGreaterThan(Slate.MAXIMUM_FEE) === true) { + + // Reject error + reject(Language.getDefaultTranslation('The fee is invalid.')); + + // Return + return; + } + + // Otherwise + else { + + // Resolve + resolve([ + + // Fee + fee, + + // Amount + amount, + + // Base fee + baseFee + ]); + + // Return + return; + } + } + + // Otherwise check if at the last transaction or there were no transactions + else if(i === transactions["length"] - 1 || transactions["length"] === 0) { + + // Check if no more transactions exist + if(transactions["length"] !== Api.SEND_TRANSACTIONS_GROUP_SIZE) { + + // Reject error + reject(Language.getDefaultTranslation('Insufficient balance.')); + + // Return + return; + } + } + } + + // Return getting fee, amount, and base fee + return getFeeAmountAndBaseFee(transactionIndex + Api.SEND_TRANSACTIONS_GROUP_SIZE).then(function(feeAmountAndBaseFee) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve fee, amount, and base fee + resolve(feeAmountAndBaseFee); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return getting fee, amount, and base fee + return getFeeAmountAndBaseFee(0).then(function(feeAmountAndBaseFee) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve fee, amount, and base fee + resolve(feeAmountAndBaseFee); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Send + send(wallet, url, amount, fee, baseFee = Api.DEFAULT_BASE_FEE, numberOfConfirmations = Api.DEFAULT_NUMBER_OF_CONFIRMATIONS, message = SlateParticipant.NO_MESSAGE, lockHeight = Slate.NO_LOCK_HEIGHT, relativeHeight = Slate.NO_RELATIVE_HEIGHT, timeToLiveCutOffHeight = Slate.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT, sendAsFile = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get current height + var currentHeight = self.node.getCurrentHeight().getHeight(); + + // Check if current height doesn't exist or the node isn't synced + if(currentHeight === Node.UNKNOWN_HEIGHT || currentHeight.isEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The current height is unknown.'))); + } + + // Otherwise + else { + + // Check if amount is invalid + if(amount.isLessThan(Slate.MINIMUM_AMOUNT) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Amount is invalid.'))); + + // Return + return; + } + + // Check if fee is invalid + if(fee.isLessThan(Slate.MINIMUM_FEE) === true || fee.isGreaterThan(Slate.MAXIMUM_FEE) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The fee is invalid.'))); + + // Return + return; + } + + // Check if base fee is invalid + if(baseFee.isLessThan(Api.MINIMUM_BASE_FEE) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Base fee is invalid.'))); + + // Return + return; + } + + // Check if time to live cut off height exists + if(timeToLiveCutOffHeight !== Slate.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT) { + + // Change relative time to live cut off height to be absolute + timeToLiveCutOffHeight = currentHeight.plus(timeToLiveCutOffHeight); + + // Check if time to live cut off height isn't greater than the current height + if(timeToLiveCutOffHeight.isLessThanOrEqualTo(currentHeight) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Time to live cut off height must be greater than the current height.'))); + + // Return + return; + } + + // Check if lock height exists and time to live cut off height isn't greater than or equal to it + if(lockHeight.isEqualTo(Slate.NO_LOCK_HEIGHT) === false && timeToLiveCutOffHeight.isLessThan(lockHeight) === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Time to live cut off height must be greater than or equal to the lock height.'))); + + // Return + return; + } + } + + // Check if relative height exists and no recent duplicate kernels isn't enabled or the relative height is invalid + if(relativeHeight !== Slate.NO_RELATIVE_HEIGHT && (Consensus.isNoRecentDuplicateKernelsEnabled(wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) === false || relativeHeight.isLessThan(SlateKernel.MINIMUM_RECENT_HEIGHT) === true || relativeHeight.isGreaterThan(SlateKernel.MAXIMUM_RECENT_HEIGHT) === true)) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Relative height is invalid.'))); + + // Return + return; + } + + // Initialize inputs + var inputs = []; + + // Initialize total amount + var totalAmount = new BigNumber(0); + + // Initialize updated transactions + var updatedTransactions = []; + + // Get inputs + var getInputs = function(transactionIndex) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return get all wallet's received released transactions + return self.transactions.getWalletsReceivedReleasedTransactions(wallet.getKeyPath(), transactionIndex, Api.SEND_TRANSACTIONS_GROUP_SIZE).then(function(transactions) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Go through all transactions or run at least once + for(var i = 0; i < transactions["length"] || i === 0; ++i) { + + // Check if a transaction exists + if(transactions["length"] !== 0) { + + // Get transaction + var transaction = transactions[i]; + + // Append transaction to inputs + inputs.push([ + + // Amount + transaction.getAmount(), + + // Identifier + transaction.getIdentifier(), + + // Switch type + transaction.getSwitchType(), + + // Features + (transaction.getIsCoinbase() === true) ? SlateInput.COINBASE_FEATURES : SlateInput.PLAIN_FEATURES, + + // Commit + transaction.getCommit(), + + // Key path + transaction.getKeyPath() + ]); + + // Update total amount + totalAmount = totalAmount.plus(transaction.getAmount()); + + // Set that transaction's status is locked + transaction.setStatus(Transaction.STATUS_LOCKED); + + // Append transaction to list + updatedTransactions.push(transaction); + } + + // Check if total amount is enough to cover the amount and fee + if(totalAmount.isGreaterThanOrEqualTo(amount.plus(fee)) === true) { + + // Resolve + resolve(); + + // Return + return; + } + + // Otherwise check if at the last transaction or there were no transactions + else if(i === transactions["length"] - 1 || transactions["length"] === 0) { + + // Check if no more transactions exist + if(transactions["length"] !== Api.SEND_TRANSACTIONS_GROUP_SIZE) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Insufficient balance.'))); + + // Return + return; + } + } + } + + // Return getting inputs + return getInputs(transactionIndex + Api.SEND_TRANSACTIONS_GROUP_SIZE).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(error)); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return getting inputs + return getInputs(0).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Trim url + url = url.trim(); + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Clear send as MQS + var sendAsMqs = false; + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set send as MQS to if the URL is an MQS address with host + var sendAsMqs = Mqs.isValidAddressWithHost(url, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) === true; + + // Break + break; + } + + // Check if not sending as file or as MQS + if(sendAsFile === false && sendAsMqs === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Get receiver's Tor address from URL + Tor.getTorAddressFromUrl(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Set receiver URL to the URL with a Tor protocol and top-level domain added if needed + var receiverUrl = ((Common.urlContainsProtocol(url) === false) ? Common.HTTP_PROTOCOL + "//" : "") + url + ((Common.urlContainsProtocol(url) === false && Common.urlContainsTopLevelDomain(url) === false) ? Tor.URL_TOP_LEVEL_DOMAIN : ""); + } + + // Otherwise + else { + + // Set receiver URL to url + var receiverUrl = url; + } + + // break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Parse the URL as a Slatepack address + var receiverPublicKey = Slatepack.slatepackAddressToPublicKey(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Set receiver URL to the receiver's public key as a Tor address with a Tor protocol and top-level domain added + var receiverUrl = Common.HTTP_PROTOCOL + "//" + Tor.publicKeyToTorAddress(receiverPublicKey) + Tor.URL_TOP_LEVEL_DOMAIN; + } + + // Otherwise + else { + + // Set receiver URL to url + var receiverUrl = url; + } + + // Break + break; + } + + // Check if receiver URL doesn't have a protocol + if(Common.urlContainsProtocol(receiverUrl) === false) { + + // Add protocol to receiver URL + receiverUrl = Common.HTTP_PROTOCOL + "//" + receiverUrl; + } + + // Try + try { + + // Parse receiver URL + new URL(Common.upgradeApplicableInsecureUrl(receiverUrl)); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Recipient address isn\'t supported.'))); + + // Return + return; + } + } + + // Otherwise + else { + + // Set receiver URL to url + var receiverUrl = url; + } + + // Return checking if receiver is compatible + return self.isCompatible(receiverUrl, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, sendAsFile, cancelOccurred).then(function(compatibleSlateVersions) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get receiver address + var getReceiverAddress = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Get receiver's public key from URL + var receiverPublicKey = Tor.torAddressToPublicKey(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Resolve the receiver's public key as a Tor address + resolve(Tor.publicKeyToTorAddress(receiverPublicKey)); + } + + // Otherwise + else { + + // Clear error occurred + errorOccurred = false; + + // Try + try { + + // Get receiver's public key from URL + receiverPublicKey = Mqs.mqsAddressToPublicKey(url, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Resolve the receiver's public key as an MQS address + resolve(Mqs.publicKeyToMqsAddress(receiverPublicKey, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)); + } + + // Otherwise + else { + + // Set use proof address to if version three, three B, and Slatepack slates are supported + var useProofAddress = compatibleSlateVersions.indexOf("V" + Slate.VERSION_THREE.toFixed()) !== Common.INDEX_NOT_FOUND || compatibleSlateVersions.indexOf("V" + Slate.VERSION_THREE.toFixed() + "B") !== Common.INDEX_NOT_FOUND || compatibleSlateVersions.indexOf(Slate.VERSION_SLATEPACK) !== Common.INDEX_NOT_FOUND; + + // Check if using proof address + if(useProofAddress === true) { + + // Return getting proof address + return self.getProofAddress(receiverUrl, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, sendAsFile, cancelOccurred).then(function(receiverAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve receiver address + resolve(receiverAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + } + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Get receiver's public key from URL + var receiverPublicKey = Slatepack.slatepackAddressToPublicKey(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Resolve the receiver's public key as a Slatepack address + resolve(Slatepack.publicKeyToSlatepackAddress(receiverPublicKey)); + } + + // Otherwise + else { + + // Return getting proof address + return self.getProofAddress(receiverUrl, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, sendAsFile, cancelOccurred).then(function(receiverAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve receiver address + resolve(receiverAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Get receiver's public key from URL + var receiverPublicKey = Tor.torAddressToPublicKey(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Resolve the receiver's public key as a Tor address + resolve(Tor.publicKeyToTorAddress(receiverPublicKey)); + } + + // Otherwise + else { + + // Set use proof address to if version three slates are supported + var useProofAddress = compatibleSlateVersions.indexOf("V" + Slate.VERSION_THREE.toFixed()) !== Common.INDEX_NOT_FOUND; + + // Check if using proof address + if(useProofAddress === true) { + + // Return getting proof address + return self.getProofAddress(receiverUrl, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, sendAsFile, cancelOccurred).then(function(receiverAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve receiver address + resolve(receiverAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + } + + // Break + break; + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return getting receiver address + return getReceiverAddress().then(function(receiverAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if payment proof is required and receiver address doesn't exist + if(self.requirePaymentProof === true && receiverAddress === Api.NO_PROOF_ADDRESS) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Recipient doesn\'t support payment proofs.'))); + } + + // Otherwise + else { + + // Get sender address + var getSenderAddress = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set use Tor proof address to if version three B or Slatepack slates are supported + var useTorProofAddress = compatibleSlateVersions.indexOf("V" + Slate.VERSION_THREE.toFixed() + "B") !== Common.INDEX_NOT_FOUND || compatibleSlateVersions.indexOf(Slate.VERSION_SLATEPACK) !== Common.INDEX_NOT_FOUND; + + // Check if proof address is supported by the receiver + if(receiverAddress !== Api.NO_PROOF_ADDRESS) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if use Tor proof address + if(useTorProofAddress === true) { + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return wallet getting MQS proof address + return wallet.getMqsProofAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return wallet getting Slatepack proof address + return wallet.getSlatepackProofAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if use Tor proof address + if(useTorProofAddress === true) { + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return wallet getting MQS proof address + return wallet.getMqsProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return wallet getting Slatepack proof address + return wallet.getSlatepackProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return wallet getting Tor proof address + return wallet.getTorProofAddress((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve sender address + resolve(senderAddress); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Resolve no sender address + resolve(Slate.NO_SENDER_ADDRESS); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return getting sender address + return getSenderAddress().then(function(senderAddress) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Update message + var updateMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if can be canceled + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Disable message + self.message.disable(); + + // Return replace message + return self.message.replace(Api.SENDER_ADDRESS_MESSAGE, senderAddress).then(function(replaceResult) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a replacement message was displayed + if(replaceResult !== Message.REPLACE_NOT_DISPLAYED_RESULT) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return updating message + return updateMessage().then(function() { + + // Try + try { + + // Check if proof address is supported by the receiver + if(receiverAddress !== Api.NO_PROOF_ADDRESS) { + + // Create slate with payment proof + var slate = new Slate(amount, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, fee, currentHeight.plus(1), lockHeight, relativeHeight, timeToLiveCutOffHeight, senderAddress, receiverAddress, compatibleSlateVersions); + } + + // Otherwise + else { + + // Create slate without payment proof + var slate = new Slate(amount, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, fee, currentHeight.plus(1), lockHeight, relativeHeight, timeToLiveCutOffHeight, Slate.NO_SENDER_ADDRESS, Slate.NO_RECEIVER_ADDRESS, compatibleSlateVersions); + } + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + + // Return + return; + } + + // Check if not sending as file or as MQS and the slate's version isn't supported + if(sendAsFile === false && sendAsMqs === false && compatibleSlateVersions.indexOf((slate.getVersion() instanceof BigNumber === true) ? "V" + slate.getVersion().toFixed() : slate.getVersion()) === Common.INDEX_NOT_FOUND) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Recipient doesn\'t support any available slate versions.'))); + } + + // Otherwise + else { + + // Make ID unique + var makeIdUnique = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return getting a transaction for the wallet with the same ID + return self.transactions.getWalletsTransactionWithId(wallet.getKeyPath(), slate.getId()).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a transaction for the wallet with the same ID doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Change slate's ID + slate.changeId(); + + // Return making ID unique + return makeIdUnique().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return making ID unique + return makeIdUnique().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get returned amount by subtracting the amount and fees from the total amount + var returnedAmount = totalAmount.minus(amount.plus(fee)); + + // Set number of change outputs + var numberOfChangeOutputs = (returnedAmount.isZero() === true) ? 0 : 1; + + // Return adding inputs to the slate + return Slate.addInputsAsynchronous(slate, inputs.map(function(input) { + + // Return slate input from the input + return new SlateInput(input[Wallet.INPUT_FEATURES_INDEX], input[Wallet.INPUT_COMMIT_INDEX]); + + }), true, numberOfChangeOutputs + 1).then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get output + var getOutput = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if returned amount is zero + if(returnedAmount.isZero() === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Build output + var buildOutput = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return wallet building output + return wallet.buildOutput(returnedAmount, (lockHeight.isEqualTo(Slate.NO_LOCK_HEIGHT) === true || lockHeight.isLessThan(currentHeight.plus(1)) === true) ? currentHeight.plus(1) : lockHeight, HardwareWallet.SENDING_TRANSACTION_MESSAGE, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return getting a transaction with the output's commit + return self.transactions.getTransaction(wallet.getWalletType(), wallet.getNetworkType(), output[Wallet.OUTPUT_COMMIT_INDEX]).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a transaction with the same commit doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return building output + return buildOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve output + resolve(output); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return getting output + return getOutput().then(function(output) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Add output + var addOutput = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Try + try { + + // Create a slate output from the output + var slateOutput = new SlateOutput(output[Wallet.OUTPUT_FEATURES_INDEX], output[Wallet.OUTPUT_COMMIT_INDEX], output[Wallet.OUTPUT_PROOF_INDEX]); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + + // Return + return; + } + + // Return adding output to slate + return Slate.addOutputsAsynchronous(slate, [slateOutput]).then(function(slate) { + + // Resolve slate + resolve(slate); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + }); + } + + // Otherwise + else { + + // Resolve slate + resolve(slate); + } + }); + }; + + // Return adding output + return addOutput().then(function(slate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Create offset + var createOffset = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Create slate offset + slate.createOffset(); + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Try + try { + + // Get slate's kernel offset + var kernelOffset = slate.getOffsetExcess(); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + + // Return + return; + } + + // Return getting a received transaction for the wallet with the kernel offset + return self.transactions.getWalletsReceivedTransactionWithKernelOffset(wallet.getKeyPath(), kernelOffset).then(function(transaction) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a received transaction for the wallet with the same kernel offset doesn't exist + if(transaction === Transactions.NO_TRANSACTION_FOUND) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return creating offset + return createOffset().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return creating offset + return createOffset().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Initialize secret key + var secretKey; + + // Initialize secret nonce + var secretNonce; + + // Initialize secret nonce index + var secretNonceIndex; + + // Add slate participant + var addSlateParticipant = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return getting wallet's sum of outputs and inputs + return wallet.getSum( + + // Outputs + (returnedAmount.isZero() === false) ? [output] : [], + + // Inputs + inputs + + ).then(function(sum) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return applying offset to slate + return slate.applyOffset(sum).then(function(offset) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Securely clear the sum + sum.fill(0); + + // Set secret key to the offset + secretKey = offset; + + // Check if creating a secret nonce was successful + secretNonce = Secp256k1Zkp.createSecretNonce(); + + if(secretNonce !== Secp256k1Zkp.OPERATION_FAILED) { + + // Return adding participant to slate + return slate.addParticipant(secretKey, secretNonce, message, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear the secret nonce and secret key + secretNonce.fill(0); + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Securely clear the secret key + secretKey.fill(0); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Securely clear the sum + sum.fill(0); + + // Securely clear the offset + offset.fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear the sum + sum.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Securely clear the sum + sum.fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return starting transaction for the output amount and total amount with the wallet's hardware wallet + return wallet.getHardwareWallet().startTransaction(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX, (returnedAmount.isZero() === false) ? output[Wallet.OUTPUT_AMOUNT_INDEX] : new BigNumber(0), totalAmount.minus(fee), fee, HardwareWallet.NO_SECRET_NONCE_INDEX, receiverAddress, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set include transaction parts + var includeTransactionParts = new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Initialize including transaction parts + var includingTransactionParts = [includeTransactionParts]; + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Include next transaction part after previous part is included + includeTransactionParts = includeTransactionParts.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return including output in the transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().includeOutputInTransaction(output[Wallet.OUTPUT_AMOUNT_INDEX], output[Wallet.OUTPUT_IDENTIFIER_INDEX], output[Wallet.OUTPUT_SWITCH_TYPE_INDEX], (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject hardware disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + + // Append including trasnaction part to list + includingTransactionParts.push(includeTransactionParts); + } + + // Go through all inputs + for(var i = 0; i < inputs["length"]; ++i) { + + // Get input + let input = inputs[i]; + + // Include next transaction part after previous part is included + includeTransactionParts = includeTransactionParts.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return including input in the transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().includeInputInTransaction(input[Wallet.INPUT_AMOUNT_INDEX], input[Wallet.INPUT_IDENTIFIER_INDEX], input[Wallet.INPUT_SWITCH_TYPE_INDEX], (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject hardware disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + + // Append including trasnaction part to list + includingTransactionParts.push(includeTransactionParts); + } + + // Return including all transaction parts in the transaction + return Promise.all(includingTransactionParts).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return applying offset to slate + return slate.applyOffset(wallet.getHardwareWallet(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(transactionSecretNonceIndex) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Set secret nonce index + secretNonceIndex = transactionSecretNonceIndex; + + // Save slate's receiver signature + var oldReceiverSignature = slate.getReceiverSignature(); + + // Return adding participant to slate + return slate.addParticipant(wallet.getHardwareWallet(), Slate.NO_SECRET_NONCE, message, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return completing transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().completeTransaction().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Remove participant from slate + slate.getParticipants().pop(); + + // Restore slate's old receiver signature + slate.setReceiverSignature(oldReceiverSignature); + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return adding a slate participant + return addSlateParticipant().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return getting slate response + return self.getSlateResponse(receiverUrl, wallet, slate, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, sendAsFile, cancelOccurred).then(function(slateResponse) { + + // Get timestamp + var timestamp = Date.now(); + + // Get prices + var prices = self.prices.getPrices(); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate is compact + if(slate.isCompact() === true) { + + // Check if combining the slate response's offset with the slate's offset failed + if(slateResponse.combineOffsets(slate.getOffset()) === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + + // Return + return; + } + } + + // Finalize slate + var finalizeSlate = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Update message + var updateMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if can be canceled + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Disable message + self.message.disable(); + + // Return replace message + return self.message.replace(Api.FINALIZE_TRANSACTION_MESSAGE, [slateResponse.getReceiverAddress(), slateResponse.getDisplayKernelFeatures()]).then(function(replaceResult) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a replacement message was displayed + if(replaceResult !== Message.REPLACE_NOT_DISPLAYED_RESULT) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return updating message + return updateMessage().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if can be canceled + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Message first button click API event + $(self.message).one(Message.FIRST_BUTTON_CLICK_EVENT + ".api", function() { + + // Turn off message second button click API event + $(self.message).off(Message.SECOND_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message hide API event + $(self.message).off(Message.HIDE_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Message second button click API event + }).one(Message.SECOND_BUTTON_CLICK_EVENT + ".api", function() { + + // Turn off message first button click API event + $(self.message).off(Message.FIRST_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message hide API event + $(self.message).off(Message.HIDE_EVENT + ".api"); + + // Finalizing the slate response + slateResponse.finalize(secretKey, secretNonce, baseFee, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, true).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Message hide API event + }).one(Message.HIDE_EVENT + ".api", function() { + + // Turn off message first button click API event + $(self.message).off(Message.FIRST_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message second button click API event + $(self.message).off(Message.SECOND_BUTTON_CLICK_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Return finalizing the slate response + return slateResponse.finalize(secretKey, secretNonce, baseFee, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, true).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return starting transaction for the output amount and total amount with the wallet's hardware wallet + return wallet.getHardwareWallet().startTransaction(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX, (returnedAmount.isZero() === false) ? output[Wallet.OUTPUT_AMOUNT_INDEX] : new BigNumber(0), totalAmount.minus(fee), fee, secretNonceIndex, receiverAddress, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set include transaction parts + var includeTransactionParts = new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Initialize including transaction parts + var includingTransactionParts = [includeTransactionParts]; + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Include next transaction part after previous part is included + includeTransactionParts = includeTransactionParts.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return including output in the transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().includeOutputInTransaction(output[Wallet.OUTPUT_AMOUNT_INDEX], output[Wallet.OUTPUT_IDENTIFIER_INDEX], output[Wallet.OUTPUT_SWITCH_TYPE_INDEX], (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject hardware disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + + // Append including trasnaction part to list + includingTransactionParts.push(includeTransactionParts); + } + + // Go through all inputs + for(var i = 0; i < inputs["length"]; ++i) { + + // Get input + let input = inputs[i]; + + // Include next transaction part after previous part is included + includeTransactionParts = includeTransactionParts.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return including input in the transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().includeInputInTransaction(input[Wallet.INPUT_AMOUNT_INDEX], input[Wallet.INPUT_IDENTIFIER_INDEX], input[Wallet.INPUT_SWITCH_TYPE_INDEX], (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject hardware disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + + // Append including trasnaction part to list + includingTransactionParts.push(includeTransactionParts); + } + + // Return including all transaction parts in the transaction + return Promise.all(includingTransactionParts).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return applying offset to slate + return slate.applyOffset(wallet.getHardwareWallet(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Update message + var updateMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if can be canceled + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Disable message + self.message.disable(); + + // Return replace message + return self.message.replace(Api.FINALIZE_TRANSACTION_MESSAGE, [slateResponse.getReceiverAddress(), slateResponse.getDisplayKernelFeatures()]).then(function(replaceResult) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a replacement message was displayed + if(replaceResult !== Message.REPLACE_NOT_DISPLAYED_RESULT) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return updating message + return updateMessage().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return finalizing the slate response + return slateResponse.finalize(wallet.getHardwareWallet(), Slate.NO_SECRET_NONCE, baseFee, wallet.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE, true, (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return completing transaction with the wallet's hardware wallet + return wallet.getHardwareWallet().completeTransaction().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Restore slate response kernel's old excess and excess signature + slateResponse.getKernels()[0].setExcess(slate.getKernels()[0].getExcess()); + slateResponse.getKernels()[0].setExcessSignature(slate.getKernels()[0].getExcessSignature()); + + // Restore slate response sender participant's old partial signature + slateResponse.getParticipant(SlateParticipant.SENDER_ID).setPartialSignature(slate.getParticipant(SlateParticipant.SENDER_ID).getPartialSignature()); + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if the user rejected on the hardware wallet + else if(error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Approving the transaction on the hardware wallet was denied.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if the user rejected on the hardware wallet + else if(error === HardwareWallet.USER_REJECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Approving the transaction on the hardware wallet was denied.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return canceling transaction with the wallet's hardware wallet and catch errors + return wallet.getHardwareWallet().cancelTransaction().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if canceled + else if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Finalizing the slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return finalizing slate + return finalizeSlate().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if can be canceled + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Disable message + self.message.disable(); + } + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Set will broadcast to if the slate's lock height doesn't exist or it can be in the next block + var willBroadcast = slateResponse.getLockHeight().isEqualTo(Slate.NO_LOCK_HEIGHT) === true || slateResponse.getLockHeight().isLessThanOrEqualTo(currentHeight.plus(1)) === true; + + // Check if returned amount isn't zero + if(returnedAmount.isZero() === false) { + + // Check if slate's height is unknown + if(slateResponse.getHeight() === Slate.UNKNOWN_HEIGHT) { + + // Set spendable height to the slate's lock height added to the number of confirmation if it exists + var spendableHeight = (slateResponse.getLockHeight().isEqualTo(Slate.NO_LOCK_HEIGHT) === false) ? slateResponse.getLockHeight().plus(numberOfConfirmations.minus(1)) : Transaction.UNKNOWN_SPENDABLE_HEIGHT; + } + + // Otherwise + else { + + // Set spendable height to the slate's height added to the number of confirmation + var spendableHeight = slateResponse.getHeight().plus(numberOfConfirmations.minus(1)); + + // Check if the slate's lock height added to the number of confirmation is greater than the spendable height + if(slateResponse.getLockHeight().isEqualTo(Slate.NO_LOCK_HEIGHT) === false && slateResponse.getLockHeight().plus(numberOfConfirmations.minus(1)).isGreaterThan(spendableHeight) === true) { + + // Set the spendable height to the slate's lock height added to the number of confirmation + spendableHeight = slateResponse.getLockHeight().plus(numberOfConfirmations.minus(1)); + } + } + + // Try + try { + + // Create returned transaction + var returnedTransaction = new Transaction(wallet.getWalletType(), wallet.getNetworkType(), output[Wallet.OUTPUT_COMMIT_INDEX], wallet.getKeyPath(), true, timestamp, timestamp, (slateResponse.getHeight() === Slate.UNKNOWN_HEIGHT) ? Transaction.UNKNOWN_HEIGHT : slateResponse.getHeight(), (slateResponse.getLockHeight().isEqualTo(Slate.NO_LOCK_HEIGHT) === false) ? slateResponse.getLockHeight() : Transaction.NO_LOCK_HEIGHT, false, Transaction.STATUS_UNCONFIRMED, returnedAmount, false, slateResponse.getExcess(), output[Wallet.OUTPUT_IDENTIFIER_INDEX], output[Wallet.OUTPUT_SWITCH_TYPE_INDEX], false, slate.getOffsetExcess(), Transaction.UNUSED_ID, Transaction.NO_MESSAGE, (slateResponse.getTimeToLiveCutOffHeight() !== Slate.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT) ? slateResponse.getTimeToLiveCutOffHeight() : Transaction.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT, false, Transaction.NO_CONFIRMED_TIMESTAMP, Transaction.NO_FEE, Transaction.NO_SENDER_ADDRESS, Transaction.NO_RECEIVER_ADDRESS, Transaction.NO_RECEIVER_SIGNATURE, Transaction.UNUSED_DESTINATION, spendableHeight, numberOfConfirmations, Transaction.UNUSED_SPENT_OUTPUTS, Transaction.UNUSED_CHANGE_OUTPUTS, willBroadcast, Transaction.UNKNOWN_REBROADCAST_MESSAGE, Transaction.UNUSED_FILE_RESPONSE, Transaction.UNUSED_PRICES_WHEN_RECORDED); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating transaction failed.'))); + + // Return + return; + } + + // Append returned transaction to list + updatedTransactions.push(returnedTransaction); + } + + // Get broadcast message + var broadcastMessage = slateResponse.getTransaction(); + + // Try + try { + + // Create sent transaction + var sentTransaction = new Transaction(wallet.getWalletType(), wallet.getNetworkType(), Transaction.UNUSED_COMMIT, wallet.getKeyPath(), false, timestamp, timestamp, (slateResponse.getHeight() === Slate.UNKNOWN_HEIGHT) ? Transaction.UNKNOWN_HEIGHT : slateResponse.getHeight(), (slateResponse.getLockHeight().isEqualTo(Slate.NO_LOCK_HEIGHT) === false) ? slateResponse.getLockHeight() : Transaction.NO_LOCK_HEIGHT, false, Transaction.UNKNOWN_STATUS, amount, false, slateResponse.getExcess(), Transaction.UNKNOWN_IDENTIFIER, Transaction.UNKNOWN_SWITCH_TYPE, true, Transaction.UNUSED_KERNEL_OFFSET, slateResponse.getId(), (slateResponse.getParticipant(SlateParticipant.SENDER_ID).getMessage() !== SlateParticipant.NO_MESSAGE) ? slateResponse.getParticipant(SlateParticipant.SENDER_ID).getMessage() : Transaction.NO_MESSAGE, (slateResponse.getTimeToLiveCutOffHeight() !== Slate.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT) ? slateResponse.getTimeToLiveCutOffHeight() : Transaction.NO_TIME_TO_LIVE_CUT_OFF_HEIGHT, false, Transaction.NO_CONFIRMED_TIMESTAMP, slateResponse.getFee(), (slateResponse.getSenderAddress() !== Slate.NO_SENDER_ADDRESS) ? slateResponse.getSenderAddress() : Transaction.NO_SENDER_ADDRESS, (slateResponse.getReceiverAddress() !== Slate.NO_RECEIVER_ADDRESS) ? slateResponse.getReceiverAddress() : Transaction.NO_RECEIVER_ADDRESS, (slateResponse.getReceiverSignature() !== Slate.NO_RECEIVER_SIGNATURE) ? slateResponse.getReceiverSignature() : Transaction.NO_RECEIVER_SIGNATURE, (url["length"] !== 0) ? url : Transaction.UNUSED_DESTINATION, Transaction.UNKNOWN_SPENDABLE_HEIGHT, Transaction.UNKNOWN_REQUIRED_NUMBER_OF_CONFIRMATIONS, inputs.map(function(input) { + + // Return input's key path + return input[Wallet.INPUT_KEY_PATH_INDEX]; + + }), (numberOfChangeOutputs === 0) ? [] : numberOfChangeOutputs, willBroadcast, JSONBigNumber.stringify(broadcastMessage), Transaction.UNUSED_FILE_RESPONSE, (prices !== Prices.NO_PRICES_FOUND) ? prices : Transaction.UNKNOWN_PRICES_WHEN_RECORDED); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating transaction failed.'))); + + // Return + return; + } + + // Append sent transaction to list + updatedTransactions.push(sentTransaction); + + // Check if broadcasting transaction + if(willBroadcast === true) { + + // Return broadcasting transaction to the node + return self.node.broadcastTransaction(broadcastMessage).then(function() { + + // Resolve + resolve([ + + // Locked amount + totalAmount, + + // Unconfirmed amount + returnedAmount, + + // Updated transactions + updatedTransactions + ]); + + // Catch errors + }).catch(function(error) { + + // Check if error contains a message + if(Node.isMessageError(error) === true && error[Node.ERROR_RESPONSE_INDEX]["Err"]["Internal"]["length"] !== 0) { + + // Get is raw data + var isRawData = Common.hasWhitespace(error[Node.ERROR_RESPONSE_INDEX]["Err"]["Internal"]) === false; + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Broadcasting the transaction failed for the following reason.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.escapeText(error[Node.ERROR_RESPONSE_INDEX]["Err"]["Internal"])) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak()); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Broadcasting the transaction failed.'))); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve([ + + // Locked amount + totalAmount, + + // Unconfirmed amount + returnedAmount, + + // Updated transactions + updatedTransactions + ]); + } + } + + // Otherwise + else { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Securely clear the secret nonce + secretNonce.fill(0); + + // Securely clear the secret key + secretKey.fill(0); + } + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Foreign API URL + static get FOREIGN_API_URL() { + + // Return foreign API URL + return "/v" + Api.CURRENT_FOREIGN_API_VERSION.toFixed() + "/foreign"; + } + + // No additional data + static get NO_ADDITIONAL_DATA() { + + // Return no additional data + return null; + } + + // Check version method + static get CHECK_VERSION_METHOD() { + + // Return check version method + return "check_version"; + } + + // Build coinbase method + static get BUILD_COINBASE_METHOD() { + + // Return build coinbase method + return "build_coinbase"; + } + + // Receive transaction method + static get RECEIVE_TRANSACTION_METHOD() { + + // Return receive transaction method + return "receive_tx"; + } + + // Get proof address method + static get GET_PROOF_ADDRESS_METHOD() { + + // Return get proof address method + return "get_proof_address"; + } + + // Response response index + static get RESPONSE_RESPONSE_INDEX() { + + // Return response response index + return 0; + } + + // Response method index + static get RESPONSE_METHOD_INDEX() { + + // Return response response index + return Api.RESPONSE_RESPONSE_INDEX + 1; + } + + // Response additional data index + static get RESPONSE_ADDITIONAL_DATA_INDEX() { + + // Return response additional data index + return Api.RESPONSE_METHOD_INDEX + 1; + } + + // Receive transaction additional data slate index + static get RECEIVE_TRANSACTION_ADDITIONAL_DATA_SLATE_INDEX() { + + // Return receive transaction additional data slate index + return 0; + } + + // Receive transaction additional data commit index + static get RECEIVE_TRANSACTION_ADDITIONAL_DATA_COMMIT_INDEX() { + + // Return receive transaction additional data commit index + return Api.RECEIVE_TRANSACTION_ADDITIONAL_DATA_SLATE_INDEX + 1; + } + + // Receive transaction additional data identifier index + static get RECEIVE_TRANSACTION_ADDITIONAL_DATA_IDENTIFIER_INDEX() { + + // Return receive transaction additional data identifier index + return Api.RECEIVE_TRANSACTION_ADDITIONAL_DATA_COMMIT_INDEX + 1; + } + + // Receive transaction additional data switch type index + static get RECEIVE_TRANSACTION_ADDITIONAL_DATA_SWITCH_TYPE_INDEX() { + + // Return receive transaction additional data switch type index + return Api.RECEIVE_TRANSACTION_ADDITIONAL_DATA_IDENTIFIER_INDEX + 1; + } + + // Fee fee index + static get FEE_FEE_INDEX() { + + // Return fee fee index + return 0; + } + + // Fee amount index + static get FEE_AMOUNT_INDEX() { + + // Return fee amount index + return Api.FEE_FEE_INDEX + 1; + } + + // Fee base fee index + static get FEE_BASE_FEE_INDEX() { + + // Return fee base fee index + return Api.FEE_AMOUNT_INDEX + 1; + } + + // Send locked amount index + static get SEND_LOCKED_AMOUNT_INDEX() { + + // Return send locked amount index + return 0; + } + + // Send unconfirmed amount index + static get SEND_UNCONFIRMED_AMOUNT_INDEX() { + + // Return send unconfirmed amount index + return Api.SEND_LOCKED_AMOUNT_INDEX + 1; + } + + // Send updated transactions index + static get SEND_UPDATED_TRANSACTIONS_INDEX() { + + // Return send updated transactions index + return Api.SEND_UNCONFIRMED_AMOUNT_INDEX + 1; + } + + // All amount + static get ALL_AMOUNT() { + + // Return all amount + return null; + } + + // Default base fee + static get DEFAULT_BASE_FEE() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Return default base fee + return new BigNumber(Consensus.VALUE_NUMBER_BASE).dividedToIntegerBy(1000); + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return default base fee + return new BigNumber(Consensus.VALUE_NUMBER_BASE).dividedToIntegerBy(100).dividedToIntegerBy(20); + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return default base fee + return new BigNumber(Consensus.VALUE_NUMBER_BASE).dividedToIntegerBy(1000); + } + } + + // Minimum base fee + static get MINIMUM_BASE_FEE() { + + // Return minimum base fee + return new BigNumber(1); + } + + // Default number of confirmations + static get DEFAULT_NUMBER_OF_CONFIRMATIONS() { + + // Return default number of confirmations + return new BigNumber(10); + } + + // Finalize transaction message + static get FINALIZE_TRANSACTION_MESSAGE() { + + // Return finalize transaction message + return "ApiFinalizeTransactionMessage"; + } + + // Finalize transaction message receiver address index + static get FINALIZE_TRANSACTION_MESSAGE_RECEIVER_ADDRESS_INDEX() { + + // Return finalize transaction message receiver address index + return 0; + } + + // Finalize transaction message kernel features index + static get FINALIZE_TRANSACTION_MESSAGE_KERNEL_FEATURES_INDEX() { + + // Return finalize transaction message kernel features index + return Api.FINALIZE_TRANSACTION_MESSAGE_RECEIVER_ADDRESS_INDEX + 1; + } + + // Get transaction response message + static get GET_TRANSACTION_RESPONSE_MESSAGE() { + + // Return get transaction response message + return "ApiGetTransactionResponseMessage"; + } + + // Get transaction response message file contents index + static get GET_TRANSACTION_RESPONSE_MESSAGE_FILE_CONTENTS_INDEX() { + + // Return get transaction response message file contents index + return 0; + } + + // Get transaction response message file name index + static get GET_TRANSACTION_RESPONSE_MESSAGE_FILE_NAME_INDEX() { + + // Return get transaction response message file name index + return Api.GET_TRANSACTION_RESPONSE_MESSAGE_FILE_CONTENTS_INDEX + 1; + } + + // Sender address message + static get SENDER_ADDRESS_MESSAGE() { + + // Return sender address message + return "ApiSenderAddressMessage"; + } + + // Private + + // Is compatible + isCompatible(url, isMainnet, sendAsFile = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not sending as file + if(sendAsFile === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Clear send as MQS + var sendAsMqs = false; + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set send as MQS to if the URL is an MQS address with host + var sendAsMqs = Mqs.isValidAddressWithHost(url, isMainnet) === true; + + // Break + break; + } + + // Check if not sending to MQS address + if(sendAsMqs === false) { + + // Get proxy request + var proxyRequest = Tor.isTorUrl(url) === true && Tor.isSupported() === false; + + // Upgrade URL if applicable + url = Common.upgradeApplicableInsecureUrl(url); + + // Return sending JSON-RPC request to check version + return JsonRpc.sendRequest(((proxyRequest === true) ? self.torProxy.getAddress() : "") + Common.removeTrailingSlashes(url) + Api.FOREIGN_API_URL, Api.CHECK_VERSION_METHOD, [], {}, JsonRpc.DEFAULT_NUMBER_OF_ATTEMPTS, cancelOccurred).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response contains a result + if(Object.isObject(response) === true && "Ok" in response === true) { + + // Set response to its value + response = response["Ok"]; + + // Check if response's foreign API version isn't supported + if(Object.isObject(response) === false || "foreign_api_version" in response === false || (Common.isNumberString(response["foreign_api_version"]) === false && response["foreign_api_version"] instanceof BigNumber === false) || (new BigNumber(response["foreign_api_version"])).isInteger() === false || (new BigNumber(response["foreign_api_version"])).isLessThan(Api.FOREIGN_API_VERSION_ONE) === true) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Get foreign API version + var foreignApiVersion = new BigNumber(response["foreign_api_version"]); + + // Check if foreign API version isn't supported + if(foreignApiVersion.isEqualTo(Api.CURRENT_FOREIGN_API_VERSION) === false) { + + // Reject unsupported foreign API version + reject(Message.createText(Language.getDefaultTranslation('Recipient\'s foreign API version isn\'t supported.'))); + + // Return + return; + } + + // Check if response's supported slate versions isn't supported + if(Object.isObject(response) === false || "supported_slate_versions" in response === false || Array.isArray(response["supported_slate_versions"]) === false || response["supported_slate_versions"].every(function(supportedSlateVersion) { + + // Return if supported slate version is a string + return typeof supportedSlateVersion === "string"; + + }) === false) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Get supported slate versions + var supportedSlateVersions = response["supported_slate_versions"]; + + // Initialize compatible slate versions + var compatibleSlateVersions = []; + + // Go through all supported slate versions + for(var i = 0; i < supportedSlateVersions["length"]; ++i) { + + // Get supported slate version + var supportedSlateVersion = supportedSlateVersions[i]; + + // Check if supported slate version is compatible + if(Slate.SUPPORTED_VERSIONS.indexOf(supportedSlateVersion) !== Common.INDEX_NOT_FOUND) { + + // Append supported slate version to list of compatible slate versions + compatibleSlateVersions.push(supportedSlateVersion); + } + } + + // Check if there no supported slate versions are compatible + if(compatibleSlateVersions["length"] === 0) { + + // Reject unsupported slate versions + reject(Message.createText(Language.getDefaultTranslation('Recipient\'s slate versions aren\'t supported.'))); + + // Return + return; + } + + // Resolve compatible slate versions + resolve(compatibleSlateVersions); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(responseStatusOrResponse) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response status is provided + if(typeof responseStatusOrResponse === "number") { + + // Reject status as text + reject(self.statusToText(responseStatusOrResponse, url, proxyRequest)); + } + + // Otherwise check if response contains an error message + else if(Object.isObject(responseStatusOrResponse) === true && "message" in responseStatusOrResponse === true && typeof responseStatusOrResponse["message"] === "string" && responseStatusOrResponse["message"]["length"] !== 0) { + + // Get is raw data + var isRawData = Common.hasWhitespace(responseStatusOrResponse["message"]) === false; + + // Reject the response's error message + reject(Message.createText(Language.getDefaultTranslation('The recipient responded with the following invalid response.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.escapeText(responseStatusOrResponse["message"])) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak()); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve compatible slate versions + resolve([]); + } + } + + // Otherwise + else { + + // Resolve compatible slate versions + resolve([]); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Get proof address + getProofAddress(url, isMainnet, sendAsFile = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not sending as file + if(sendAsFile === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Clear send as MQS + var sendAsMqs = false; + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set send as MQS to if the URL is an MQS address with host + var sendAsMqs = Mqs.isValidAddressWithHost(url, isMainnet) === true; + + // Break + break; + } + + // Check if not sending to MQS address + if(sendAsMqs === false) { + + // Get proxy request + var proxyRequest = Tor.isTorUrl(url) === true && Tor.isSupported() === false; + + // Upgrade URL if applicable + url = Common.upgradeApplicableInsecureUrl(url); + + // Return sending JSON-RPC request to get proof address + return JsonRpc.sendRequest(((proxyRequest === true) ? self.torProxy.getAddress() : "") + Common.removeTrailingSlashes(url) + Api.FOREIGN_API_URL, Api.GET_PROOF_ADDRESS_METHOD, [], {}, JsonRpc.DEFAULT_NUMBER_OF_ATTEMPTS, cancelOccurred).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response contains a result + if(Object.isObject(response) === true && "Ok" in response === true) { + + // Set response to its value + response = response["Ok"]; + + // Check if response isn't supported + if(typeof response !== "string") { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Get proof address + var proofAddress = response.replace(Common.DOUBLE_QUOTE_PATTERN, ""); + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check proof address's length + switch(proofAddress["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Try + try { + + // Get public key from proof address + Tor.torAddressToPublicKey(proofAddress); + } + + // Catch errors + catch(error) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Try + try { + + // Get public key from proof address + Mqs.mqsAddressToPublicKey(proofAddress, isMainnet); + } + + // Catch errors + catch(error) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // Default + default: + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check proof address's length + switch(proofAddress["length"]) { + + // Slatepack address length + case Slatepack.ADDRESS_LENGTH: + + // Try + try { + + // Get public key from proof address + Slatepack.slatepackAddressToPublicKey(proofAddress); + } + + // Catch errors + catch(error) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // Default + default: + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check proof address's length + switch(proofAddress["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Try + try { + + // Get public key from proof address + Tor.torAddressToPublicKey(proofAddress); + } + + // Catch errors + catch(error) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + + // Default + default: + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + + // Return + return; + } + + // Break + break; + } + + // Resolve proof address + resolve(proofAddress); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(responseStatusOrResponse) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response status is provided + if(typeof responseStatusOrResponse === "number") { + + // Check if the status is ok or bad request + if(responseStatusOrResponse === Common.HTTP_OK_STATUS || responseStatusOrResponse === Common.HTTP_BAD_REQUEST_STATUS) { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + + // Otherwise + else { + + // Reject status as text + reject(self.statusToText(responseStatusOrResponse, url, proxyRequest)); + } + } + + // Otherwise check if response is a method not found error + else if(Object.isObject(responseStatusOrResponse) === true && "code" in responseStatusOrResponse && responseStatusOrResponse["code"] instanceof BigNumber === true && responseStatusOrResponse["code"].isEqualTo(JsonRpc.METHOD_NOT_FOUND_ERROR) === true) { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + + // Otherwise check if response contains an error message + else if(Object.isObject(responseStatusOrResponse) === true && "message" in responseStatusOrResponse === true && typeof responseStatusOrResponse["message"] === "string" && responseStatusOrResponse["message"]["length"] !== 0) { + + // Get is raw data + var isRawData = Common.hasWhitespace(responseStatusOrResponse["message"]) === false; + + // Reject the response's error message + reject(Message.createText(Language.getDefaultTranslation('The recipient responded with the following invalid response.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.escapeText(responseStatusOrResponse["message"])) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak()); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + } + + // Otherwise + else { + + // Resolve no proof address + resolve(Api.NO_PROOF_ADDRESS); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Get slate response + getSlateResponse(url, wallet, slate, isMainnet, sendAsFile = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Try + try { + + // Serialzie the slate + var serializedSlate = slate.serialize(isMainnet, Slate.COMPACT_SLATE_PURPOSE_SEND_INITIAL, sendAsFile === true); + } + + // Catch errors + catch(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + + // Return + return; + } + + // Encode slate + var encodeSlate = function(serializedSlate) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if serialized slate is binary + if(serializedSlate instanceof Uint8Array === true) { + + // Check if a slate has a Tor receiver address + if(slate.getReceiverAddress() !== Slate.NO_RECEIVER_ADDRESS && slate.getReceiverAddress()["length"] === Tor.ADDRESS_LENGTH) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return getting wallet's Tor secret key + return wallet.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(secretKey) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate, secretKey, Tor.torAddressToPublicKey(slate.getReceiverAddress())).then(function(slatepack) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate, wallet.getHardwareWallet(), Tor.torAddressToPublicKey(slate.getReceiverAddress()), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return encoding the serialized slate + return encodeSlate(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Return encoding the serialized slate + return Slatepack.encodeSlatepack(serializedSlate).then(function(slatepack) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the Slatepack + resolve(slatepack); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Resolve the serialized slate + resolve(serializedSlate); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return encoding the slate + return encodeSlate(serializedSlate).then(function(encodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Send slate + var sendSlate = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not sending as file + if(sendAsFile === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Clear send as MQS + var sendAsMqs = false; + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set send as MQS to if the URL is an MQS address with host + var sendAsMqs = Mqs.isValidAddressWithHost(url, isMainnet) === true; + + // Break + break; + } + + // Check if not sending to MQS address + if(sendAsMqs === false) { + + // Get proxy request + var proxyRequest = Tor.isTorUrl(url) === true && Tor.isSupported() === false; + + // Upgrade URL if applicable + url = Common.upgradeApplicableInsecureUrl(url); + + // Set current slate send ID to the slate's ID + self.currentSlateSendId = slate.getId(); + + // Return sending JSON-RPC request to get slate response + return JsonRpc.sendRequest(((proxyRequest === true) ? self.torProxy.getAddress() : "") + Common.removeTrailingSlashes(url) + Api.FOREIGN_API_URL, Api.RECEIVE_TRANSACTION_METHOD, [ + + // Slate + encodedSlate, + + // Destination account name + null, + + // Message + null, + + ], {}, JsonRpc.DEFAULT_NUMBER_OF_ATTEMPTS, cancelOccurred).then(function(response) { + + // Set current slate send ID to no current slate send ID + self.currentSlateSendId = Api.NO_CURRENT_SLATE_SEND_ID; + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response contains a result + if(Object.isObject(response) === true && "Ok" in response === true) { + + // Resolve response's result + resolve(response["Ok"]); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(responseStatusOrResponse) { + + // Set current slate send ID to no current slate send ID + self.currentSlateSendId = Api.NO_CURRENT_SLATE_SEND_ID; + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response status is provided + if(typeof responseStatusOrResponse === "number") { + + // Reject status as text + reject(self.statusToText(responseStatusOrResponse, url, proxyRequest)); + } + + // Otherwise check if response contains an error message + else if(Object.isObject(responseStatusOrResponse) === true && "message" in responseStatusOrResponse === true && typeof responseStatusOrResponse["message"] === "string" && responseStatusOrResponse["message"]["length"] !== 0) { + + // Get is raw data + var isRawData = Common.hasWhitespace(responseStatusOrResponse["message"]) === false; + + // Reject the response's error message + reject(Message.createText(Language.getDefaultTranslation('The recipient responded with the following invalid response.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.escapeText(responseStatusOrResponse["message"])) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak()); + } + + // Otherwise + else { + + // Reject invalid response + reject(Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Create ephemeral secret key + var ephemeralSecretKey = new Uint8Array(Crypto.SECP256K1_SECRET_KEY_LENGTH); + + // While ephemeral secret key isn't a valid secret key + do { + + // Fill ephemeral secret key with random values + crypto.getRandomValues(ephemeralSecretKey); + + } while(Secp256k1Zkp.isValidSecretKey(ephemeralSecretKey) !== true); + + // Return sending MQS request to get slate response + return Mqs.sendRequest(url, encodedSlate, ephemeralSecretKey, isMainnet, cancelOccurred).then(function(response) { + + // Securely clear ephemeral secret key + ephemeralSecretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear ephemeral secret key + ephemeralSecretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Initialize response + var response = Api.NO_RESPONSE; + + // Initialize response error + var responseError = Api.NO_RESPONSE_ERROR; + + // Update message + var updateMessage = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Disable message + self.message.disable(); + + // Set file contents + var fileContents = (typeof encodedSlate === "string") ? encodedSlate : JSONBigNumber.stringify(encodedSlate); + + // Set file name + var fileName = slate.getId().serialize(); + + // Message before show not cancelable API event + $(self.message).one(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api", function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Message message display second button click API event + self.message.messageDisplay.find("button").eq(1).on("click.api", function(event) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response and response error don't exist + if(response === Api.NO_RESPONSE && responseError === Api.NO_RESPONSE_ERROR) { + + // Stop immediate propagation + event.stopImmediatePropagation(); + + // Get button + var button = $(this); + + // Check if button has focus + if(button.is(":focus") === true) { + + // Add focus apperance to button + button.addClass("focus"); + } + + // Blue focused element + $(":focus").blur(); + + // Remove selection + Focus.removeSelection(); + + // Show loading + self.application.showLoading(); + + // Set that message second button is loading + self.message.setButtonLoading(Message.SECOND_BUTTON); + + // Disable message + self.message.disable(); + + // Prevent scrolling keys + self.application.scroll.preventKeys(); + + // Block input + $("body").addClass("blockInput"); + + // Open file + var openFile = function(file) { + + // Create file reader + var fileReader = new FileReader(); + + // File reader load event + $(fileReader).one("load", function(event) { + + // Turn off file reader error event + $(fileReader).off("error"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get file's contents trimmed + var filesContents = event["originalEvent"]["target"]["result"].trim(); + + // Try + try { + + // Get response from file's contents parsed as JSON + response = JSONBigNumber.parse(filesContents); + } + + // Catch errors + catch(error) { + + // Set response to file's contents + response = filesContents; + } + + // Trigger click on button + button.trigger("click"); + } + + // File reader error event + }).one("error", function() { + + // Turn off file reader load event + $(fileReader).off("load"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set response error to error + responseError = Message.createText(Language.getDefaultTranslation('Opening that file failed.')); + + // Trigger click on button + button.trigger("click"); + } + }); + + // Read file as text with file reader + fileReader.readAsText(file); + }; + + // Open file canceled + var openFileCanceled = function() { + + // Allow scrolling keys + self.application.scroll.allowKeys(); + + // Unblock input + $("body").removeClass("blockInput"); + + // Hide loading + self.application.hideLoading(); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Enable message + self.message.enable(); + + // Check if button had focus + if(button.hasClass("focus") === true) { + + // Focus on button and remove its focus apperance + button.focus().removeClass("focus"); + } + } + }; + + // Check if File System API is supported + if(typeof showOpenFilePicker === "function") { + + // Show open file picker + showOpenFilePicker().then(function(fileHandles) { + + // Allow scrolling keys + self.application.scroll.allowKeys(); + + // Unblock input + $("body").removeClass("blockInput"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if no file was selected + if(fileHandles["length"] <= 0) { + + // Hide loading + self.application.hideLoading(); + + // Enable message + self.message.enable(); + + // Check if button had focus + if(button.hasClass("focus") === true) { + + // Focus on button + button.focus().removeClass("focus"); + } + } + + // Otherwise + else { + + // Get selected file + fileHandles[0].getFile().then(function(file) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Open file + openFile(file); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Set response error to error + responseError = Message.createText(Language.getDefaultTranslation('Opening that file failed.')); + + // Trigger click on button + button.trigger("click"); + } + }); + } + } + + // Otherwise + else { + + // Hide loading + self.application.hideLoading(); + } + + // Catch errors + }).catch(function(error) { + + // Open file canceled + openFileCanceled(); + }); + } + + // Otherwise + else { + + // Create file input + var fileInput = $(""); + + // Set file selected to false + var fileSelected = false; + + // File input change event + fileInput.one("change", function(event) { + + // Set file selected + fileSelected = true; + + // Turn off window focus API event + $(window).off("focus.api"); + + // Allow scrolling keys + self.application.scroll.allowKeys(); + + // Unblock input + $("body").removeClass("blockInput"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Get files + var files = event["target"]["files"]; + + // Check if no file was selected + if(files["length"] <= 0) { + + // Hide loading + self.application.hideLoading(); + + // Enable message + self.message.enable(); + + // Check if button had focus + if(button.hasClass("focus") === true) { + + // Focus on button + button.focus().removeClass("focus"); + } + } + + // Otherwise + else { + + // Get file + var file = files[0]; + + // Open file + openFile(file); + } + } + + // Otherwise + else { + + // Hide loading + self.application.hideLoading(); + } + }); + + // Window focus API event + $(window).one("focus.api", function() { + + // Set timeout + setTimeout(function() { + + // Check if a file isn't selected + if(fileSelected === false) { + + // Turn off file input change event + fileInput.off("change"); + + // Open file canceled + openFileCanceled(); + } + }, Api.FILE_INPUT_CANCEL_CHECK_DELAY_MILLISECONDS); + }); + + // Trigger file input selection + fileInput.trigger("click"); + } + } + } + }); + } + }); + + // Return replace message + return self.message.replace(Api.GET_TRANSACTION_RESPONSE_MESSAGE, [fileContents, fileName]).then(function(replaceResult) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if a replacement message was displayed + if(replaceResult !== Message.REPLACE_NOT_DISPLAYED_RESULT) { + + // Save file contents + Common.saveFile(fileName, fileContents); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return updating message + return updateMessage().then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Message first button click API event + $(self.message).one(Message.FIRST_BUTTON_CLICK_EVENT + ".api", function() { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Turn off message second button click API event + $(self.message).off(Message.SECOND_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message hide API event + $(self.message).off(Message.HIDE_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Message second button click API event + }).one(Message.SECOND_BUTTON_CLICK_EVENT + ".api", function() { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Turn off message first button click API event + $(self.message).off(Message.FIRST_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message hide API event + $(self.message).off(Message.HIDE_EVENT + ".api"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response exists + if(response !== Api.NO_RESPONSE) { + + // Resolve response + resolve(response); + } + + // Otherwise check if response error exists + else if(responseError !== Api.NO_RESPONSE_ERROR) { + + // Reject response error + reject(responseError); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Message hide API event + }).one(Message.HIDE_EVENT + ".api", function() { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Turn off message first button click API event + $(self.message).off(Message.FIRST_BUTTON_CLICK_EVENT + ".api"); + + // Turn off message second button click API event + $(self.message).off(Message.SECOND_BUTTON_CLICK_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Turn off message before show not cancelable event API event + $(self.message).off(Message.BEFORE_SHOW_NOT_CANCELABLE_EVENT + ".api"); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return sending slate + return sendSlate().then(function(response) { + + // Decode slate + var decodeSlate = function(serializedSlateOrSlatepack) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Set expecting Slatepack + var expectingSlatepack = slate.isCompact() === true; + + // break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Set expecting Slatepack + var expectingSlatepack = sendAsFile === true; + + // break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set expecting Slatepack + var expectingSlatepack = false; + + // Break + break; + } + + // Check if a Slatepack is received and it should have been + if(typeof serializedSlateOrSlatepack === "string" && expectingSlatepack === true) { + + // Get Slatepack + var slatepack = serializedSlateOrSlatepack; + + // Check if Slatepack should be encrypted, it is encrypted, and it's sender public key matches the slate's receiver address + if(slate.getReceiverAddress() !== Slate.NO_RECEIVER_ADDRESS && slate.getReceiverAddress()["length"] === Tor.ADDRESS_LENGTH && Slatepack.isEncryptedSlatepack(slatepack) === true && Slatepack.getSlatepackSenderPublicKey(slatepack) !== Slatepack.NO_PUBLIC_KEY && Tor.publicKeyToTorAddress(Slatepack.getSlatepackSenderPublicKey(slatepack)) === slate.getReceiverAddress()) { + + // Check if wallet isn't a hardware wallet + if(wallet.getHardwareType() === Wallet.NO_HARDWARE_TYPE) { + + // Return getting wallet's Tor secret key + return wallet.getAddressKey(Wallet.PAYMENT_PROOF_TOR_ADDRESS_KEY_INDEX).then(function(secretKey) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack, secretKey).then(function(decodedSlate) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return waiting for wallet's hardware wallet to connect + return self.wallets.waitForHardwareWalletToConnect(wallet.getKeyPath(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Connect the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Connect the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack, wallet.getHardwareWallet(), (wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Unlock the hardware wallet for Wallet %1$s to continue sending the payment.') : Language.getDefaultTranslation('Unlock the hardware wallet for %1$y to continue sending the payment.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()], false, true, cancelOccurred).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet was disconnected + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Wallet's hardware wallet disconnect event + $(wallet.getHardwareWallet()).one(HardwareWallet.DISCONNECT_EVENT, function() { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + } + + // Otherwise + else { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Return decoding the Slatepack + return decodeSlate(slatepack).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if response should have been encrypted but it wasn't encrypted or has the wrong sender public key + else if(slate.getReceiverAddress() !== Slate.NO_RECEIVER_ADDRESS && slate.getReceiverAddress()["length"] === Tor.ADDRESS_LENGTH) { + + // Reject + reject(); + } + + // Otherwise check if response was encrypted when it shouldn't have been + else if(Slatepack.isEncryptedSlatepack(slatepack) === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Return decoding the Slatepack + return Slatepack.decodeSlatepack(slatepack).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if a Slatepack should have been received but it wasn't + else if(expectingSlatepack === true) { + + // Reject + reject(); + } + + // Otherwise check if a serialized slate was received + else if(Object.isObject(serializedSlateOrSlatepack) === true) { + + // Get decoded slate + var decodedSlate = serializedSlateOrSlatepack; + + // Resolve the decoded slate + resolve(decodedSlate); + } + + // Otherwise + else { + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }; + + // Return decoding response + return decodeSlate(response).then(function(decodedSlate) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return parsing slate + return Slate.parseSlateAsynchronous(decodedSlate, isMainnet, Slate.COMPACT_SLATE_PURPOSE_SEND_RESPONSE, slate).then(function(slateResponse) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if slate response isn't for the slate + if(slate.isEqualTo(slateResponse) === false) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Otherwise check if no new outputs were added to the slate response + else if(slateResponse.getOutputs()["length"] <= slate.getOutputs()["length"]) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Otherwise check if a participant is missing from the slate response + else if(slateResponse.getNumberOfParticipants().isEqualTo(slateResponse.getParticipants()["length"]) === false) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Check if slate response's receiver participant doesn't exist + else if(slateResponse.getParticipant(SlateParticipant.SENDER_ID.plus(1)) === Slate.NO_PARTICIPANT) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Otherwise check if slate response's receiver participant isn't complete + else if(slateResponse.getParticipant(SlateParticipant.SENDER_ID.plus(1)).isComplete() === false) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Check if slate response has a payment proof but no receiver signature + else if(slateResponse.hasPaymentProof() === true && slateResponse.getReceiverSignature() === Slate.NO_RECEIVER_SIGNATURE) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Otherwise + else { + + // Resolve slate response + resolve(slateResponse); + } + + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject unsupported response + reject(Message.createText(Language.getDefaultTranslation('Unsupported response from the recipient.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Creating slate failed.'))); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Status to text + statusToText(status, url, proxyRequest) { + + // Check status + switch(status) { + + // HTTP ok status + case Common.HTTP_OK_STATUS: + + // Return invalid response + return Message.createText(Language.getDefaultTranslation('Invalid response from the recipient.')); + + // HTTP unauthorized status + case Common.HTTP_UNAUTHORIZED_STATUS: + + // Return unauthorized response + return Message.createText(Language.getDefaultTranslation('Unauthorized response from the recipient.')); + + // HTTP forbidden status + case Common.HTTP_FORBIDDEN_STATUS: + + // Return forbidden response + return Message.createText(Language.getDefaultTranslation('Forbidden response from the recipient.')); + + // HTTP not found status + case Common.HTTP_NOT_FOUND_STATUS: + + // Return not found response + return Message.createText(Language.getDefaultTranslation('Not found response from the recipient.')); + + // HTTP unsupported media type status + case Common.HTTP_UNSUPPORTED_MEDIA_TYPE_STATUS: + + // Return invalid request response + return Message.createText(Language.getDefaultTranslation('Invalid request response from the recipient.')); + + // HTTP payload too large status + case Common.HTTP_PAYLOAD_TOO_LARGE_STATUS: + + // Return payload too large response + return Message.createText(Language.getDefaultTranslation('Payload too large response from the recipient.')); + + // HTTP bad gateway status or HTTP gateway timeout status + case Common.HTTP_BAD_GATEWAY_STATUS: + case Common.HTTP_GATEWAY_TIMEOUT_STATUS: + + // Check if request was proxied + if(proxyRequest === true) { + + // Check if using a custom Tor proxy + if(this.torProxy.usingCustomTorProxy() === true) { + + // Check if Tor proxy isn't set + if(this.torProxy.getAddress()["length"] === 0) { + + // Return connecting failed with Tor proxy information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You\'ll need to provide a Tor proxy address to connect to the recipient.')); + } + + // Otherwise + else { + + // Return connecting failed with Tor proxy information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a different Tor proxy address to connect to the recipient.')); + } + } + + // Otherwise + else { + + // Return connecting failed + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')); + } + } + + // Otherwise + else { + + // Return error response + return Message.createText(Language.getDefaultTranslation('Error response from the recipient.')); + } + + // Break + break; + + // HTTP no response status + case Common.HTTP_NO_RESPONSE_STATUS: + + // Check if not an extension and the page is connected to securely + if(Common.isExtension() === false && (location["protocol"] === Common.HTTPS_PROTOCOL || Tor.isOnionService() === true)) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Parse URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if URL will be connected to insecurely + if(parsedUrl["protocol"] === Common.HTTP_PROTOCOL && Tor.isTorUrl(url) === false) { + + // Check if is an app + if(Common.isApp() === true) { + + // Return connecting failed with insecure content information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.')); + } + + // Otherwise + else { + + // Return connecting failed with insecure content information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a recipient address that is served over HTTPS or as an Onion Service to connect to the recipient.')); + } + } + } + } + + // Check if using a custom Tor proxy and request was proxied + if(this.torProxy.usingCustomTorProxy() === true && proxyRequest === true) { + + // Check if Tor proxy isn't set + if(this.torProxy.getAddress()["length"] === 0) { + + // Return connecting failed with Tor proxy information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You\'ll need to provide a Tor proxy address to connect to the recipient.')); + } + + // Otherwise + else { + + // Return connecting failed with Tor proxy information + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a different Tor proxy address to connect to the recipient.')); + } + } + + // Otherwise + else { + + // Return no response + return Message.createText(Language.getDefaultTranslation('Connecting to the recipient failed.')); + } + + // Break + break; + + // Default + default: + + // Return error response + return Message.createText(Language.getDefaultTranslation('Error response from the recipient.')); + } + } + + // Foreign API version one + static get FOREIGN_API_VERSION_ONE() { + + // Return foreign API version one + return 1; + } + + // Foreign API version two + static get FOREIGN_API_VERSION_TWO() { + + // Return foreign API version two + return 2; + } + + // Current foreign API version + static get CURRENT_FOREIGN_API_VERSION() { + + // Return current foreign API version + return Api.FOREIGN_API_VERSION_TWO; + } + + // Check version parameters length + static get CHECK_VERSION_PARAMETERS_LENGTH() { + + // Return check version parameters length + return 0; + } + + // Receive transaction parameters length + static get RECEIVE_TRANSACTION_PARAMETERS_LENGTH() { + + // Return receive transaction parameters length + return 3; + } + + // Receive transaction slate parameter + static get RECEIVE_TRANSACTION_SLATE_PARAMETER_INDEX() { + + // Return receive transaction slate parameter index + return 0; + } + + // Get proof address parameters length + static get GET_PROOF_ADDRESS_PARAMETERS_LENGTH() { + + // Return get proof address parameters length + return 0; + } + + // Coinbase slate version + static get COINBASE_SLATE_VERSION() { + + // Return coinbase slate version + return Slate.VERSION_TWO; + } + + // Receive transaction expected number of slate participants + static get RECEIVE_TRANSACTION_EXPECTED_NUMBER_OF_SLATE_PARTICIPANTS() { + + // Return receive transaction expected number of slate participants + return 2; + } + + // Receive transaction expected number of slate kernels + static get RECEIVE_TRANSACTION_EXPECTED_NUMBER_OF_SLATE_KERNELS() { + + // Return receive transaction expected number of slate kernels + return 1; + } + + // Send transactions group size + static get SEND_TRANSACTIONS_GROUP_SIZE() { + + // Return send transactions group size + return 500; + } + + // No proof address + static get NO_PROOF_ADDRESS() { + + // Return no proof address + return null; + } + + // Settings enable mining API name + static get SETTINGS_ENABLE_MINING_API_NAME() { + + // Return settings enable mining API name + return "Enable Mining API"; + } + + // Settings enable mininig API default value + static get SETTINGS_ENABLE_MINING_API_DEFAULT_VALUE() { + + // Return settings enable mininig API default value + return false; + } + + // Settings require payment proof name + static get SETTINGS_REQUIRE_PAYMENT_PROOF_NAME() { + + // Return settings require payment proof name + return "Require Payment Proof"; + } + + // Settings require payment proof default value + static get SETTINGS_REQUIRE_PAYMENT_PROOF_DEFAULT_VALUE() { + + // Return settings require payment proof default value + return false; + } + + // Settings automatically approve receiving payments name + static get SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_NAME() { + + // Return settings automatically approve receiving payments name + return "Automatically Approve Receiving Payments"; + } + + // Settings automatically approve receiving payments default value + static get SETTINGS_AUTOMATICALLY_APPROVE_RECEIVING_PAYMENTS_DEFAULT_VALUE() { + + // Return settings automatically approve receiving payments default value + return true; + } + + // Current slate send ID + static get NO_CURRENT_SLATE_SEND_ID() { + + // Return current slate send ID + return null; + } + + // No response + static get NO_RESPONSE() { + + // Return no response + return null; + } + + // No response error + static get NO_RESPONSE_ERROR() { + + // Return no response error + return null; + } + + // File input cancel check delay milliseconds + static get FILE_INPUT_CANCEL_CHECK_DELAY_MILLISECONDS() { + + // Return file input cancel check delay milliseconds + return 500; + } +} + + +// Main function + +// Set global object's API +globalThis["Api"] = Api; diff --git a/scripts/application.js b/scripts/application.js new file mode 100755 index 0000000..e303c71 --- /dev/null +++ b/scripts/application.js @@ -0,0 +1,7566 @@ +// Use strict +"use strict"; + + +// Classes + +// Application class +class Application { + + // Public + + // Constructor + constructor() { + + // Get body display + this.bodyDisplay = $("body"); + + // Get main display + this.mainDisplay = $("main"); + + // Get locked display + this.lockedDisplay = $("div.locked"); + + // Get unlocked display + this.unlockedDisplay = $("div.unlocked"); + + // Get loading display + this.loadingDisplay = this.lockedDisplay.find("div.loading"); + + // Get create display + this.createDisplay = this.lockedDisplay.find("div.create"); + + // Get unlock display + this.unlockDisplay = this.lockedDisplay.find("div.unlock"); + + // Get status display + this.statusDisplay = this.unlockDisplay.find("div.status"); + + // Get info display + this.infoDisplay = this.lockedDisplay.find("div.info"); + + // Create settings + this.settings = new Settings(); + + // Create focus + this.focus = new Focus(); + + // Create clipboard + this.clipboard = new Clipboard(); + + // Create message + this.message = new Message(this, this.focus, this.clipboard); + + // Create caps lock + this.capsLock = new CapsLock(); + + // Create Tor proxy + this.torProxy = new TorProxy(this.settings); + + // Create node + this.node = new Node(this.torProxy, this.settings); + + // Create listener + this.listener = new Listener(this.settings); + + // Create transactions + this.transactions = new Transactions(); + + // Create prices + this.prices = new Prices(this.settings); + + // Create wallets + this.wallets = new Wallets(this.torProxy, this.node, this.listener, this.settings, this, this.message, this.transactions, this.prices); + + // Create version + this.version = new Version(this, this.message); + + // Create maintenance notification + this.maintenanceNotification = new MaintenanceNotification(); + + // Create cookie acceptance + this.cookieAcceptance = new CookieAcceptance(); + + // Create automatic lock + this.automaticLock = new AutomaticLock(this, this.message, this.settings); + + // Create install app + this.installApp = new InstallApp(this.cookieAcceptance, this.automaticLock); + + // Create service worker installer + this.serviceWorkerInstaller = new ServiceWorkerInstaller(this.version); + + // Create wake lock + this.wakeLock = new WakeLock(); + + // Create logo + this.logo = new Logo(this, this.message, this.wakeLock); + + // Create sections + this.sections = new Sections(this.settings, this.message, this.version); + + // Create scroll + this.scroll = new Scroll(); + + // Create unlocked + this.unlocked = new Unlocked(this, this.bodyDisplay, this.unlockedDisplay, this.settings, this.message, this.focus, this.wallets, this.node, this.listener, this.automaticLock, this.transactions, this.sections, this.scroll, this.wakeLock, this.clipboard, this.prices); + + // Set node incompatible message shown + this.nodeIncompatibleMessageShown = false; + + // Set node invalid response message shown + this.nodeInvalidResponseMessageShown = false; + + // Set node unauthorized response message shown + this.nodeUnauthorizedResponseMessageShown = false; + + // Set node connection message shown + this.nodeConnectionMessageShown = false; + + // Set listener connection message shown + this.listenerConnectionMessageShown = false; + + // Set ignore updates + this.ignoreUpdates = false; + + // Set enable node connection error messages to setting's default value + this.enableNodeConnectionErrorMessages = Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE; + + // Set enable listener connection error messages to setting's default value + this.enableListenerConnectionErrorMessages = Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE; + + // Set reset settings + this.resetSettings = false; + + // Set unlocked at least once + this.unlockedAtLeastOnce = false; + + // Set protocol handler registered + this.protocolHandlerRegistered = false; + + // Set self + var self = this; + + // Once database is initialized + Database.onceInitialized(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return creating settings + return Promise.all([ + + // Enable node connection error messages setting + self.settings.createValue(Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_NAME, Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE), + + // Enable listener connection error messages setting + self.settings.createValue(Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_NAME, Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE) + + ]).then(function() { + + // Initialize settings + var settings = [ + + // Enable node connection error messages setting + Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_NAME, + + // Enable listener connection error messages setting + Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_NAME + ]; + + // Return getting settings' values + return Promise.all(settings.map(function(setting) { + + // Return getting setting's value + return self.settings.getValue(setting); + + })).then(function(settingValues) { + + // Set enable node connection error messages to setting's value + self.enableNodeConnectionErrorMessages = settingValues[settings.indexOf(Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_NAME)]; + + // Set enable listener connection error messages to setting's value + self.enableListenerConnectionErrorMessages = settingValues[settings.indexOf(Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_NAME)]; + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + }); + + // Settings change event + $(this.settings).on(Settings.CHANGE_EVENT, function(event, setting) { + + // Check what setting was changes + switch(setting[Settings.DATABASE_SETTING_NAME]) { + + // Enable node connection error messages setting + case Application.SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_NAME: + + // Set enable node connection error messages to setting's value + self.enableNodeConnectionErrorMessages = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + + // Enable listener connection error messages setting + case Application.SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_NAME: + + // Set enable listener connection error messages to setting's value + self.enableListenerConnectionErrorMessages = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + } + }); + + // Check if is an extension + if(Common.isExtension() === true) { + + // Document extension request receive event + $(document).on(Extension.REQUEST_RECEIVE_EVENT, function() { + + // Check if an extension request exists, unlocked display is shown, a message isn't shown, not showing loading, unlocked isn't disabled, unlocked display isn't showing minimal display, automatic lock isn't disabled, automatic lock isn't locking, and message isn't disabled + if(Extension.getRequests()["length"] !== 0 && self.isUnlockedDisplayShown() === true && self.message.isShown() === false && self.isShowingLoading() === false && self.unlocked.isDisabled() === false && self.unlockedDisplay.hasClass("minimal") === false && self.automaticLock.getAllowed() === (AutomaticLock.NO_INTERACTION_LOCK_TYPE | AutomaticLock.INACTIVE_LOCK_TYPE) && self.automaticLock.isLocking() === false && self.message.getAllowed() === true) { + + // Show current section and catch errors + self.sections.showCurrentSection(false).catch(function(error) { + + }); + } + }); + + // Set interval + setInterval(function() { + + // Check if an extension request exists, unlocked display is shown, a message isn't shown, not showing loading, unlocked isn't disabled, unlocked display isn't showing minimal display, automatic lock isn't disabled, automatic lock isn't locking, and message isn't disabled + if(Extension.getRequests()["length"] !== 0 && self.isUnlockedDisplayShown() === true && self.message.isShown() === false && self.isShowingLoading() === false && self.unlocked.isDisabled() === false && self.unlockedDisplay.hasClass("minimal") === false && self.automaticLock.getAllowed() === (AutomaticLock.NO_INTERACTION_LOCK_TYPE | AutomaticLock.INACTIVE_LOCK_TYPE) && self.automaticLock.isLocking() === false && self.message.getAllowed() === true) { + + // Show current section and catch errors + self.sections.showCurrentSection(false).catch(function(error) { + + }); + } + + }, Application.CHECK_EXTENSION_REQUEST_RECEIVED_INTERVAL_MILLISECONDS); + } + + // Window context menu event + $(window).on("contextmenu", function(event) { + + // Check if is an app + if(Common.isApp() === true) { + + // Get element + var element = $(event["target"]); + + // Check if element isn't a radio input or a link and doesn't allow the context menu + if((element.is("input") === false || element.attr("type") === "radio") && element.is("a") === false && element.hasClass("contextMenu") === false) + + // Prevent default + event.preventDefault(); + } + + // Window device orientation change event + }).on("orientationchange", function() { + + // Check if is an app + if(Common.isApp() === true) { + + // Trigger a resize event + $(window).trigger("resize"); + } + + // Window resize event + }).on("resize", function() { + + // Check if not an app + if(Common.isApp() === false) { + + // Scroll to origin + $(window).scrollTop(0); + + // Set timeout + setTimeout(function() { + + // Scroll back to origin to prevent minimal interface mode + $(window).scrollTop(1); + $(window).scrollTop(0); + + }, Application.PREVENT_MINIMAL_INTERFACE_DELAY_MILLISECONDS); + } + + // Trigger transition end event on unlock display delete all wallets button + self.unlockDisplay.find("div.deleteAllWallets").children().trigger("transitionend"); + + // Window page show event + }).on("pageshow", function(event) { + + // Check if page was loaded form cache + if(event["originalEvent"]["persisted"] === true) { + + // Show loading + self.showLoading(); + + // Prevent extension from interrupting on close + Extension.preventInterruptOnClose(); + + // Reload page + location.reload(); + } + + // Window before unload event + }).on("beforeunload", function() { + + // Prevent showing messages + self.message.prevent(); + + // Lock wallets + self.wallets.lock(); + + // Check if unlocked display is shown + if(self.isUnlockedDisplayShown() === true) { + + // Show loading + self.showLoading(); + + // Disable unlocked + self.unlocked.disable(); + + // Prevent automatic lock + self.automaticLock.prevent(); + + // Check if message is shown + if(self.message.isShown() === true) { + + // Hide message and uninitialize + self.message.hide(true); + } + + // Otherwise + else { + + // Uninitialize message + self.message.uninitialize(); + } + + // Hide language display + $("div.language").addClass("hide normalTransitionSpeed"); + + // Hide unlocked display children + self.unlockedDisplay.children().addClass("hide"); + + // Unlocked display children transition end or timeout event + self.unlockedDisplay.children().transitionEndOrTimeout(function() { + + // Hide unlocked display + self.unlockedDisplay.addClass("hide"); + + // Hide loading + self.hideLoading(); + + // Enable unlocked + self.unlocked.enable(); + + // Reset unlocked + self.unlocked.reset(); + + }, "opacity"); + } + + // Otherwise + else { + + // Check if message is shown + if(self.message.isShown() === true) { + + // Hide message and uninitialize + self.message.hide(true); + } + + // Otherwise + else { + + // Uninitialize message + self.message.uninitialize(); + } + } + + // Window unload event + }).on("unload", function() { + + // Uninitialize dependencies + self.uninitializeDependencies(); + }); + + // Document mouse down and focus change event + $(document).on("mousedown " + Common.FOCUS_CHANGE_EVENT, function(event) { + + // Check if event isn't mouse down or the primary pointer button was pressed and the focused element isn't an input element + if((event["type"] !== "mousedown" || (event["buttons"] & Common.PRIMARY_POINTER_BUTTON_BITMASK) !== 0) && $(":focus").is("input") === false) { + + // Remove selection + Focus.removeSelection(); + } + + // Document key down application event + }).on("keydown.application", function(event) { + + // Check if shift was pressed + if(event["which"] === Application.SHIFT_KEY_CODE) { + + // Turn off document key down application event + $(document).off("keydown.application"); + + // Set reset settings + self.resetSettings = true; + } + }); + + // Node connection open event + $(this.node).on(Node.CONNECTION_OPEN_EVENT, function() { + + // Get node status display + var nodeStatusDisplay = self.statusDisplay.find("p.node"); + + // Set that node status display shows success + nodeStatusDisplay.removeClass("warning").addClass("success"); + + // Set title + var title = Language.getDefaultTranslation('Node connected'); + + // Set node status display's title + nodeStatusDisplay.attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Turn off node connection warning application event + $(self.node).off(Node.CONNECTION_WARNING_EVENT + ".application"); + + // Turn off node connection close application event + $(self.node).off(Node.CONNECTION_CLOSE_EVENT + ".application"); + + // Clear node connection message shown + self.nodeConnectionMessageShown = false; + + // Node connection warning event + }).on(Node.CONNECTION_WARNING_EVENT, function(event, warningType) { + + // Get node status display + var nodeStatusDisplay = self.statusDisplay.find("p.node"); + + // Set that node status display shows warning + nodeStatusDisplay.removeClass("success").addClass("warning"); + + // Set title + var title = Language.getDefaultTranslation('Node warning'); + + // Set node status display's title + nodeStatusDisplay.attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Turn off node connection close application event + $(self.node).off(Node.CONNECTION_CLOSE_EVENT + ".application"); + + // Check if warning is because node isn't compatible and node incompatible message hasn't been shown + if(warningType === Node.INCOMPATIBLE_WARNING_TYPE && self.nodeIncompatibleMessageShown === false) { + + // Check if showing node connection error messages is enabled + if(self.enableNodeConnectionErrorMessages === true) { + + // Show message + self.message.show(Language.getDefaultTranslation('Node Error'), Message.createText(Language.getDefaultTranslation('The node isn\'t compatible. The node\'s version must be version %1$v or newer.'), [ + + // Minimum compatible node version + Node.MINIMUM_COMPATIBLE_NODE_VERSION + + ]), false, function() { + + // Check if node incompatible message hasn't been shown and the node status is showing a warning + if(self.nodeIncompatibleMessageShown === false && nodeStatusDisplay.hasClass("warning") === true) { + + // Set node incompatible message shown + self.nodeIncompatibleMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Node connection open application node incompatible warning event + $(self.node).one(Node.CONNECTION_OPEN_EVENT + ".applicationNodeIncompatibleWarning", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off node connection open application node incompatible warning event + $(self.node).off(Node.CONNECTION_OPEN_EVENT + ".applicationNodeIncompatibleWarning"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + } + + // Otherwise check if warning is because node returned in invalid response and node invalid response message hasn't been shown + else if(warningType === Node.INVALID_RESPONSE_WARNING_TYPE && self.nodeInvalidResponseMessageShown === false) { + + // Node connection warning application event + $(self.node).one(Node.CONNECTION_WARNING_EVENT + ".application", function(event, warningType) { + + // Check if warning is because node returned in invalid response and node invalid response message hasn't been shown + if(warningType === Node.INVALID_RESPONSE_WARNING_TYPE && self.nodeInvalidResponseMessageShown === false) { + + // Check if showing node connection error messages is enabled + if(self.enableNodeConnectionErrorMessages === true) { + + // Show message + self.message.show(Language.getDefaultTranslation('Node Error'), Message.createText(Language.getDefaultTranslation('The node returned an invalid response.')), false, function() { + + // Check if node invalid response message hasn't been shown and the node status is showing a warning + if(self.nodeInvalidResponseMessageShown === false && nodeStatusDisplay.hasClass("warning") === true) { + + // Set node invalid response message shown + self.nodeInvalidResponseMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Node connection open application node invalid response warning event + $(self.node).one(Node.CONNECTION_OPEN_EVENT + ".applicationNodeInvalidResponseWarning", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off node connection open application node invalid response warning event + $(self.node).off(Node.CONNECTION_OPEN_EVENT + ".applicationNodeInvalidResponseWarning"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + } + }); + } + + // Otherwise check if warning is because node returned an unauthorized response and node unauthorized response message hasn't been shown + else if(warningType === Node.UNAUTHORIZED_WARNING_TYPE && self.nodeUnauthorizedResponseMessageShown === false) { + + // Check if showing node connection error messages is enabled + if(self.enableNodeConnectionErrorMessages === true) { + + // Check if node's secret doesn't exist + if(self.node.getSecret() === Node.NO_SECRET) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('The node returned an unauthorized response.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('The node may require a foreign API secret.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('The node returned an unauthorized response.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Verify that the node\'s foreign API secret is correct.')); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Node Error'), message, false, function() { + + // Check if node unauthorized response message hasn't been shown and the node status is showing a warning + if(self.nodeUnauthorizedResponseMessageShown === false && nodeStatusDisplay.hasClass("warning") === true) { + + // Set node unauthorized response message shown + self.nodeUnauthorizedResponseMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Node connection open application node unauthorized response warning event + $(self.node).one(Node.CONNECTION_OPEN_EVENT + ".applicationNodeIUnauthorizedResponseWarning", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off node connection open application node unauthorized response warning event + $(self.node).off(Node.CONNECTION_OPEN_EVENT + ".applicationNodeIUnauthorizedResponseWarning"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + } + + // Node connection close event + }).on(Node.CONNECTION_CLOSE_EVENT, function(event, closeType) { + + // Get node status display + var nodeStatusDisplay = self.statusDisplay.find("p.node"); + + // Set that node status display shows error + nodeStatusDisplay.removeClass("warning success"); + + // Set title + var title = Language.getDefaultTranslation('Node disconnected'); + + // Set node status display's title + nodeStatusDisplay.attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Turn off node connection warning application event + $(self.node).off(Node.CONNECTION_WARNING_EVENT + ".application"); + + // Check if close is because couldn't connect to node and node connection message hasn't been shown or the close is because the connection to the node was disconnected + if((closeType === Node.NO_CONNECTION_CLOSE_TYPE && self.nodeConnectionMessageShown === false) || closeType === Node.DISCONNECTED_CLOSE_TYPE) { + + // Node connection close application event + $(self.node).one(Node.CONNECTION_CLOSE_EVENT + ".application", function() { + + // Check if close is because couldn't connect to node and node connection message hasn't been shown or the close is because the connection to the node was disconnected + if((closeType === Node.NO_CONNECTION_CLOSE_TYPE && self.nodeConnectionMessageShown === false) || closeType === Node.DISCONNECTED_CLOSE_TYPE) { + + // Check if showing node connection error messages is enabled + if(self.enableNodeConnectionErrorMessages === true) { + + // Check if close is because couldn't connect to node + if(closeType === Node.NO_CONNECTION_CLOSE_TYPE) { + + // Check if using a custom node + if(self.node.usingCustomNode() === true) { + + // Check if node's address exists + if(self.node.getAddresses(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)[0]["length"] !== 0) { + + // Check if not an extension and the page is connected to securely + if(Common.isExtension() === false && (location["protocol"] === Common.HTTPS_PROTOCOL || Tor.isOnionService() === true)) { + + // Initialize error occurred + var errorOccurred = false; + + // Get URL as the node's first address + var url = self.node.getAddresses(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)[0]; + + // Try + try { + + // Parse URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if node will be connected to insecurely + if(parsedUrl["protocol"] === Common.HTTP_PROTOCOL && Tor.isTorUrl(url) === false) { + + // Check if is an app + if(Common.isApp() === true) { + + // Set message with insecure content information + var message = Message.createText(Language.getDefaultTranslation('Connecting to the node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.')); + } + + // Otherwise + else { + + // Set message with insecure content information + var message = Message.createText(Language.getDefaultTranslation('Connecting to the node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a node address that is served over HTTPS or as an Onion Service to connect to the node.')); + } + } + } + } + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('A node address hasn\'t been provided.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')); + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Check if using a custom node + if(self.node.usingCustomNode() === true) { + + // Check if using a custom Tor proxy + if(self.torProxy.usingCustomTorProxy() === true) { + + // Initialize error occurred + var errorOccurred = false; + + // Get URL as the node's first address + var url = self.node.getAddresses(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)[0]; + + // Try + try { + + // Parse URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if Tor proxy was used to connect to the node + if(Tor.isTorUrl(url) === true && Tor.isSupported() === false) { + + // Check if Tor proxy isn't set + if(self.torProxy.getAddress()["length"] === 0) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Connecting to the node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You\'ll need to provide a Tor proxy address to connect to the node.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Connecting to the node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a different Tor proxy address to connect to the node.')); + } + } + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Connecting to the node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')); + } + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Connecting to a node failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Node Error'), message, false, function() { + + // Check if node connection message hasn't been shown and the node status isn't showing a warning or success + if(self.nodeConnectionMessageShown === false && nodeStatusDisplay.hasClass("warning") === false && nodeStatusDisplay.hasClass("success") === false) { + + // Set node connection message shown + self.nodeConnectionMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Node connection open application node no connection error event + $(self.node).one(Node.CONNECTION_OPEN_EVENT + ".applicationNodeNoConnectionError", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Node connection open application node no connection error event + $(self.node).off(Node.CONNECTION_OPEN_EVENT + ".applicationNodeNoConnectionError"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + + // Otherwise check if close is because connection to node was disconnected + else if(closeType === Node.DISCONNECTED_CLOSE_TYPE) { + + // Check if using a custom node + if(self.node.usingCustomNode() === true) { + + // Check if using a custom Tor proxy + if(self.torProxy.usingCustomTorProxy() === true) { + + // Initialize error occurred + var errorOccurred = false; + + // Get URL as the node's first address + var url = self.node.getAddresses(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)[0]; + + // Try + try { + + // Parse URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if Tor proxy was used to connect to the node + if(Tor.isTorUrl(url) === true && Tor.isSupported() === false) { + + // Check if Tor proxy isn't set + if(self.torProxy.getAddress()["length"] === 0) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('You\'re no longer connected to the node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You\'ll need to provide a Tor proxy address to connect to the node.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('You\'re no longer connected to the node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a different Tor proxy address to connect to the node.')); + } + } + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('You\'re no longer connected to the node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')); + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('You\'re no longer connected to a node.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to send payments without being connected to a node.')); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Node Error'), message, false, function() { + + // Check if the node status isn't showing warning or success + if(nodeStatusDisplay.hasClass("warning") === false && nodeStatusDisplay.hasClass("success") === false) { + + // Set node connection message shown + self.nodeConnectionMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Node connection open application node disconnected error event + $(self.node).one(Node.CONNECTION_OPEN_EVENT + ".applicationNodeDisconnectedError", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off node connection open application node disconnected error event + $(self.node).off(Node.CONNECTION_OPEN_EVENT + ".applicationNodeDisconnectedError"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + } + } + }); + } + + // Node settings change event + }).on(Node.SETTINGS_CHANGE_EVENT, function() { + + // Clear node incompatible message shown + self.nodeIncompatibleMessageShown = false; + + // Clear node invalid response message shown + self.nodeInvalidResponseMessageShown = false; + + // Clear node unauthorized response message shown + self.nodeUnauthorizedResponseMessageShown = false; + + // Check if node's address exists + if(self.node.getAddresses(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE)[0]["length"] !== 0) { + + // Clear node connection message shown + self.nodeConnectionMessageShown = false; + } + + // Otherwise + else { + + // Set node connection message shown + self.nodeConnectionMessageShown = true; + } + + // Turn off node connection warning application event + $(self.node).off(Node.CONNECTION_WARNING_EVENT + ".application"); + + // Turn off node connection close application event + $(self.node).off(Node.CONNECTION_CLOSE_EVENT + ".application"); + }); + + // Listener connection open event + $(this.listener).on(Listener.CONNECTION_OPEN_EVENT, function() { + + // Get listener status display + var listenerStatusDisplay = self.statusDisplay.find("p.listener"); + + // Set that listener status display shows success + listenerStatusDisplay.removeClass("warning").addClass("success"); + + // Set title + var title = Language.getDefaultTranslation('Listener connected'); + + // Set listener status display's title + listenerStatusDisplay.attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Turn off listener connection close application event + $(self.listener).off(Listener.CONNECTION_CLOSE_EVENT + ".application"); + + // Clear listener connection message shown + self.listenerConnectionMessageShown = false; + + // Listener connection close event + }).on(Listener.CONNECTION_CLOSE_EVENT, function(event, closeType) { + + // Get listener status display + var listenerStatusDisplay = self.statusDisplay.find("p.listener"); + + // Set that listener status display shows error + listenerStatusDisplay.removeClass("warning success"); + + // Set title + var title = Language.getDefaultTranslation('Listener disconnected'); + + // Set listener status display's title + listenerStatusDisplay.attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Check if listener connection message hasn't been shown + if(self.listenerConnectionMessageShown === false) { + + // Listener connection close application event + $(self.listener).one(Listener.CONNECTION_CLOSE_EVENT + ".application", function() { + + // Check if listener connection message hasn't been shown + if(self.listenerConnectionMessageShown === false) { + + // Check if showing listener connection error messages is enabled + if(self.enableListenerConnectionErrorMessages === true) { + + // Check if close is because couldn't connect to listener + if(closeType === Listener.NO_CONNECTION_CLOSE_TYPE) { + + // Check if using a custom listener + if(self.listener.usingCustomListener() === true) { + + // Check if listener's address exists + if(self.listener.getAddress()["length"] !== 0) { + + // Check if not an extension and the page is connected to securely + if(Common.isExtension() === false && (location["protocol"] === Common.HTTPS_PROTOCOL || Tor.isOnionService() === true)) { + + // Initialize error occurred + var errorOccurred = false; + + // Get URL as the listener's address + var url = self.listener.getAddress(); + + // Try + try { + + // Parse URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if listener will be connected to insecurely + if(parsedUrl["protocol"] === Common.WEBSOCKET_PROTOCOL && Tor.isTorUrl(url) === false) { + + // Check if is an app + if(Common.isApp() === true) { + + // Set message with insecure content information + var message = Message.createText(Language.getDefaultTranslation('Connecting to the listener failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to receive payments without being connected to a listener.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from an app that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a listener address that is served over HTTPS to connect to the listener.')); + } + + // Otherwise + else { + + // Set message with insecure content information + var message = Message.createText(Language.getDefaultTranslation('Connecting to the listener failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to receive payments without being connected to a listener.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Some browsers don\'t allow connecting to content that is served insecurely from a site that is served securely.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to specify a listener address that is served over HTTPS to connect to the listener.')); + } + } + } + } + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('A listener address hasn\'t been provided.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to receive payments without being connected to a listener.')); + } + } + + // Check if a message hasn't been set + if(typeof message === "undefined") { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Connecting to the listener failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to receive payments without being connected to a listener.')); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Listener Error'), message, false, function() { + + // Check if listener connection message hasn't been shown and the listener status isn't showing warning + if(self.listenerConnectionMessageShown === false && listenerStatusDisplay.hasClass("warning") === false && listenerStatusDisplay.hasClass("success") === false) { + + // Set listener connection message shown + self.listenerConnectionMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Listener connection open application listener no connection error event + $(self.listener).one(Listener.CONNECTION_OPEN_EVENT + ".applicationListenerNoConnectionError", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off listener connection open application listener no connection error event + $(self.listener).off(Listener.CONNECTION_OPEN_EVENT + ".applicationListenerNoConnectionError"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + + // Otherwise check if close is because connection to listener was disconnected + else if(closeType === Listener.DISCONNECTED_CLOSE_TYPE) { + + // Show message + self.message.show(Language.getDefaultTranslation('Listener Error'), Message.createText(Language.getDefaultTranslation('You\'re no longer connected to the listener.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You won\'t be able to receive payments without being connected to a listener.')), false, function() { + + // Check if the listener status isn't showing warning + if(listenerStatusDisplay.hasClass("warning") === false && listenerStatusDisplay.hasClass("success") === false) { + + // Set listener connection message shown + self.listenerConnectionMessageShown = true; + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Listener connection open application listener disconnected error event + $(self.listener).one(Listener.CONNECTION_OPEN_EVENT + ".applicationListenerDisconnectedError", function() { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + }); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off listener connection open application listener disconnected error event + $(self.listener).off(Listener.CONNECTION_OPEN_EVENT + ".applicationListenerDisconnectedError"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + } + } + } + }); + } + + // Listener settings change event + }).on(Listener.SETTINGS_CHANGE_EVENT, function() { + + // Check if listener's address exists + if(self.listener.getAddress()["length"] !== 0) { + + // Clear listener connection message shown + self.listenerConnectionMessageShown = false; + } + + // Otherwise + else { + + // Set listener connection message shown + self.listenerConnectionMessageShown = true; + } + + // Turn off listener connection close application event + $(self.listener).off(Listener.CONNECTION_CLOSE_EVENT + ".application"); + }); + + // Wallets currency receive event + $(this.wallets).on(Wallets.CURRENCY_RECEIVE_EVENT, function(event, wallet, amount, currency, message, receiverAddress) { + + // Get is raw data + var isRawData = Common.hasWhitespace(message) === false; + + // Show message + self.message.show(Language.getDefaultTranslation('Payment Received'), Message.createSuccessResult() + Message.createLineBreak() + Message.createText((message === SlateParticipant.NO_MESSAGE || message["length"] === 0) ? ((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('You were sent %1$c to Wallet %2$s.') : Language.getDefaultTranslation('You were sent %1$c to %2$y.')) : ((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('You were sent %1$c to Wallet %2$s with a message.') : Language.getDefaultTranslation('You were sent %1$c to %2$y with a message.')), [ + + [ + + // Number + amount.toFixed(), + + // Currency + currency, + + // Display value + true + ], + + // Wallet key path or name + (wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName() + + ]) + ((message !== SlateParticipant.NO_MESSAGE && message["length"] !== 0) ? Message.createLineBreak() + Message.createLineBreak() + "" + Common.htmlEncode(message) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak() + Message.createLineBreak() : Message.createText(Language.getDefaultTranslation('(?<=.) '))) + ((receiverAddress !== Slate.NO_RECEIVER_ADDRESS) ? Message.createText(Language.getDefaultTranslation('The recipient payment proof address you used for the transaction is the following payment proof address.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Common.htmlEncode(receiverAddress) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak() : Message.createText(Language.getDefaultTranslation('The transaction doesn\'t have a payment proof.'))) + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('You shouldn\'t consider this payment to be legitimate until it\'s been confirmed on the blockchain.')) + "", false, function() { + + // Check if wallet exists + if(self.wallets.walletExists(wallet.getKeyPath()) === true) { + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, false, Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + }); + + // Service worker installer update available event + $(this.serviceWorkerInstaller).on(ServiceWorkerInstaller.UPDATE_AVAILABLE_EVENT, function() { + + // Check if not ignoring updates + if(self.ignoreUpdates === false) { + + // Check if is an app + if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('An update for this app is available. Do you want to install the update now? If so, this app will reload once the update has been installed. If not, the update will be install the next time you open this app.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('An update for this site is available. Do you want to install the update now? If so, this site will reload once the update has been installed. If not, the update will be install the next time you visit this site.'); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Update Available'), Message.createText(message), false, function() { + + // Save focus and blur + self.focus.save(true); + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + }, Language.getDefaultTranslation('No'), Language.getDefaultTranslation('Yes'), false).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if installing the update + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Prevent showing messages + self.message.prevent(); + + // Show loading + self.showLoading(); + + // Set that message second button is loading + self.message.setButtonLoading(Message.SECOND_BUTTON); + + // Delete focus + self.focus.delete(); + + // Set timeout + setTimeout(function() { + + // Check if is an app + if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('The update was successfully installed. This app will now reload to use the new version.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('The update was successfully installed. This site will now reload to use the new version.'); + } + + // Show message + self.message.show(Language.getDefaultTranslation('Update Installed'), Message.createText(message), true, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Prevent showing messages + self.message.prevent(); + + // Show loading + self.showLoading(); + + // Prevent extension from interrupting on close + Extension.preventInterruptOnClose(); + + // Reload page + location.reload(); + } + }); + + }, Application.INSTALL_UPDATE_DELAY_MILLISECONDS); + } + + // Otherwise + else { + + // Set ignore updates + self.ignoreUpdates = true; + + // Check if create display is shown + if(self.isCreateDisplayShown() === true) + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlock display is shown + else if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + } + }); + } + }); + + // Is not iframe + this.isNotIframe().then(function() { + + // If browser is compatible + self.browserIsCompatible().then(function() { + + // Install service worker + self.installServiceWorker().then(function() { + + // Show version changes + self.version.showChanges().then(function() { + + // Show private browsing message + self.showPrivateBrowsingMessage().then(function() { + + // Show third-party cookies message + self.showThirdPartyCookiesMessage().then(function() { + + // Is primary instance + self.isPrimaryInstance().then(function() { + + // Set timeout + setTimeout(function() { + + // Show reset settings + self.showResetSettings().then(function() { + + // Initialize dependencies + self.initializeDependencies().then(function() { + + // Initialize extension + self.initializeExtension().then(function() { + + // Set verify source + self.setVerifySource(); + + // Show create or unlock + self.showCreateOrUnlock(); + }); + }); + }); + }, 0); + }); + }); + }); + }); + }); + }); + }); + + // Locked display form show click event + this.lockedDisplay.find("form span.show").on("click", function(event) { + + // Get target + var target = $(this); + + // Get input + var input = target.siblings("input"); + + // Check if input isn't disabled + if(input.is(":disabled") === false) { + + // Save input selection + var savedSelectionStart = input.get(0)["selectionStart"]; + var savedSelectionEnd = input.get(0)["selectionEnd"]; + var savedSelectionDirection = input.get(0)["selectionDirection"]; + + // Check if concealing password + if(target.hasClass("conceal") === true) { + + // Set title + var title = Language.getDefaultTranslation('Show'); + + // Show reveal icon and set title + target.removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Change password input type + input.attr("type", "password"); + } + + // Otherwise + else { + + // Set title + var title = Language.getDefaultTranslation('Hide'); + + // Show conceal icon and set title + target.addClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)); + + // Change password input type + input.attr("type", "text"); + } + + // Request animation frame + requestAnimationFrame(function() { + + // Save focus and don't blur + self.focus.save(false); + + // Restore input selection + input.get(0).setSelectionRange(savedSelectionStart, savedSelectionEnd, savedSelectionDirection); + + // Restore focus and blur if it doesn't exist + self.focus.restore(true); + }); + } + + // Locked display form show mouse down event + }).on("mousedown", function(event) { + + // Get target + var target = $(this); + + // Check if target's input has focus + if(target.siblings("input").is(":focus") === true) { + + // Prevent stealing focus + event.preventDefault(); + + // Trigger focus change event + target.trigger(Common.FOCUS_CHANGE_EVENT); + } + }); + + // Locked display form input input event + this.lockedDisplay.find("form input").on("input", function() { + + // Get input + var input = $(this); + + // Check if input's value is empty + if(input.val()["length"] === 0) + + // Set that input is empty + input.removeClass("notEmpty"); + + // Otherwise + else + + // Set that input isn't empty + input.addClass("notEmpty"); + }); + + // Create display form submit event + this.createDisplay.children("form").on("submit", function(event) { + + // Prevent default + event.preventDefault(); + + // Prevent showing messages + self.message.prevent(); + + // Save focus, blur, and get focused element + var focusedElement = self.focus.save(true); + + // Check if focused element is a button + if(focusedElement !== Focus.NO_FOCUS && focusedElement.is("button") === true) + + // Set that focused element is clicked + focusedElement.addClass("clicked"); + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Show loading + self.showLoading(); + + // Set that create display button is loading + $(this).children("button").addClass("loading"); + + // Get password + var password = $(this).find("input[name=\"Password\"]").val(); + + // Get confirm password + var confirmPassword = $(this).find("input[name=\"Confirm Password\"]").val(); + + // Show create error + var showCreateError = function(error) { + + // TODO Securely clear password and confirm password + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Create Error'), Message.createText(error), false, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Set that create display button isn't loading + self.createDisplay.find("form").children("button").removeClass("loading"); + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Check if focused element is clicked + if(focusedElement !== Focus.NO_FOCUS && focusedElement.hasClass("clicked") === true) + + // Set that focused element isn't clicked + focusedElement.removeClass("clicked"); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + }; + + // Check if password is invalid + if(password["length"] === 0) + + // Show create error + showCreateError(Language.getDefaultTranslation('Password is empty.')); + + // Otherwise check if password is invalid + else if(confirmPassword["length"] === 0) + + // Show create error + showCreateError(Language.getDefaultTranslation('Confirm password is empty.')); + + // Otherwise check if passwords don't match match + else if(password !== confirmPassword) + + // Show create error + showCreateError(Language.getDefaultTranslation('Passwords don\'t match.')); + + // Otherwise + else { + + // Unlock wallets using password + self.wallets.unlock(password).then(function() { + + // Create a wallet + self.wallets.create(Wallet.NO_NAME, Consensus.getWalletType(), Consensus.getNetworkType(), (self.node.isConnected() === true) ? Wallet.STATUS_SYNCED : ((self.node.connectionFailed() === true) ? Wallet.STATUS_ERROR : Wallet.STATUS_SYNCING)).then(function(wallet) { + + // Connect wallets to node and listener if not closing when dont processing extension requests + self.wallets.connectToNodeAndListener(Extension.getCloseWhenDone() === true); + + // Get wallet's passphrase + wallet.getPassphrase().then(function(walletPassphrase) { + + // Initialize unlocked + self.unlocked.initialize().then(function() { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Created wallet Wallet %1$s.'), [ + + // Wallet key path + wallet.getKeyPath().toFixed() + ]); + + // TODO Securely clear password and confirm password + + // Log message + Log.logMessage(Language.getDefaultTranslation('Unlocked.')); + + // Delete all saved focus + self.focus.deleteAll(); + + // Allow extension to interrupt on close + Extension.allowInterruptOnClose(); + + // Set timeout + setTimeout(function() { + + // Check if message is shown + if(self.message.isShown() === true) { + + // Check if message visible state doesn't include unlocked + if((self.message.visibleState() & Message.VISIBLE_STATE_UNLOCKED) === 0) + + // Hide message + self.message.hide(); + } + + // Hide create display children + self.createDisplay.children().addClass("hide"); + + // Hide logo + self.logo.hide(); + + // Hide info display + self.infoDisplay.addClass("hide"); + + // Create display form transition end or timeout event + self.createDisplay.children("form").transitionEndOrTimeout(function() { + + // Hide create display + self.createDisplay.addClass("hide"); + + // Reset + self.reset(); + + // Hide loading + self.hideLoading(); + + // Set that create display button isn't loading + self.createDisplay.find("form").children("button").removeClass("loading"); + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Check if focused element is clicked + if(focusedElement !== Focus.NO_FOCUS && focusedElement.hasClass("clicked") === true) + + // Set that focused element isn't clicked + focusedElement.removeClass("clicked"); + + // Clear create display shown input values + self.createDisplay.find("input:not(.hide)").val("").trigger("input"); + + // Unhide create display logo + self.createDisplay.children("div.logo").removeClass("hide"); + + // Set that unlock display can show status + self.unlockDisplay.addClass("showStatus"); + + // Set unlocked at least once + self.unlockedAtLeastOnce = true; + + // Disable unlocked + self.unlocked.disable(); + + // Show unlocked display + self.unlockedDisplay.removeClass("hide"); + + // Set timeout + setTimeout(function() { + + // Trigger resize event + $(window).trigger("resize"); + + // Show unlocked display children + self.unlockedDisplay.children().removeClass("hide"); + + // Trigger section shown event on the current section + $(self.sections.getCurrentSection()).trigger(Section.SHOWN_EVENT); + + // Trigger unlocked show event + $(self.unlocked).trigger(Unlocked.SHOW_EVENT); + + // Check if protocol handler wasn't registered + if(self.protocolHandlerRegistered === false) { + + // Set protocol handler registered + self.protocolHandlerRegistered = true; + + // Register protocol handler + ProtocolHandler.register(); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('New Wallet Passphrase'), Message.createText(Language.getDefaultTranslation('This passphrase will allow you to recover Wallet %1$s. It\'s recommended that you record this passphrase in a secure, nondigital way.'), [wallet.getKeyPath().toFixed()]) + Message.createLineBreak() + Message.createLineBreak() + "" + Common.htmlEncode(walletPassphrase) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('Don\'t disclose this passphrase to anyone.')) + "", false, Message.NO_BEFORE_SHOW_FUNCTION, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // TODO Securely clear walletPassphrase + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Allow automatic lock + self.automaticLock.allow(); + + // Check if automatic lock isn't locking + if(self.automaticLock.isLocking() === false) { + + // Enable unlocked + self.unlocked.enable(); + + // Focus on unlocked display + self.unlockedDisplay.focus(); + + // Trigger section focus event on the current section + $(self.sections.getCurrentSection()).trigger(Section.FOCUS_EVENT); + + // Hide message + self.message.hide(); + } + } + }); + }, 0); + + }, "opacity"); + + }, Application.SHOW_UNLOCKED_DISPLAY_DELAY_MILLISECONDS); + + // Catch errors + }).catch(function(error) { + + // TODO Securely clear walletPassphrase + + // Reset unlocked + self.unlocked.reset(); + + // Remove wallet and catch errors + self.wallets.removeWallet(wallet.getKeyPath()).catch(function(error) { + + // Finally + }).finally(function() { + + // Lock wallets + self.wallets.lock(); + + // Show create error + showCreateError(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Remove wallet and catch errors + self.wallets.removeWallet(wallet.getKeyPath()).catch(function(error) { + + // Finally + }).finally(function() { + + // Lock wallets + self.wallets.lock(); + + // Show create error + showCreateError(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Lock wallets + self.wallets.lock(); + + // Show create error + showCreateError(error); + }); + + // Catch errors + }).catch(function(error) { + + // Lock wallets + self.wallets.lock(); + + // Show create error + showCreateError(error); + }); + } + }); + + // Unlock display form submit event + this.unlockDisplay.children("form").on("submit", function(event) { + + // Prevent default + event.preventDefault(); + + // Prevent showing messages + self.message.prevent(); + + // Save focus, blur, and get focused element + var focusedElement = self.focus.save(true); + + // Check if focused element is a button + if(focusedElement !== Focus.NO_FOCUS && focusedElement.is("button") === true) + + // Set that focused element is clicked + focusedElement.addClass("clicked"); + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Show loading + self.showLoading(); + + // Set that unlock display button is loading + $(this).children("button").addClass("loading"); + + // Get password + var password = $(this).find("input[name=\"Password\"]").val(); + + // Show unlock error + var showUnlockError = function(error) { + + // TODO Securely clear password + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Unlock Error'), Message.createText(error), false, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Set that unlock display button isn't loading + self.unlockDisplay.find("form").children("button").removeClass("loading"); + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Check if focused element is clicked + if(focusedElement !== Focus.NO_FOCUS && focusedElement.hasClass("clicked") === true) + + // Set that focused element isn't clicked + focusedElement.removeClass("clicked"); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + }; + + // Check if password is invalid + if(password["length"] === 0) + + // Show unlock error + showUnlockError(Language.getDefaultTranslation('Password is empty.')); + + // Otherwise + else { + + // Unlock wallets using password + self.wallets.unlock(password, self.unlockedAtLeastOnce === false).then(function() { + + // Connect wallets to node and listener if not closing when dont processing extension requests + self.wallets.connectToNodeAndListener(Extension.getCloseWhenDone() === true); + + // Initialize unlocked + self.unlocked.initialize().then(function() { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Unlocked.')); + + // TODO Securely clear password + + // Delete all saved focus + self.focus.deleteAll(); + + // Allow extension to interrupt on close + Extension.allowInterruptOnClose(); + + // Set timeout + setTimeout(function() { + + // Check if message is shown + if(self.message.isShown() === true) { + + // Check if message visible state doesn't include unlocked + if((self.message.visibleState() & Message.VISIBLE_STATE_UNLOCKED) === 0) + + // Hide message + self.message.hide(); + } + + // Hide unlock display children + self.unlockDisplay.children().addClass("hide"); + + // Hide logo + self.logo.hide(); + + // Hide info display + self.infoDisplay.addClass("hide"); + + // Unlock display form transition end or timeout event + self.unlockDisplay.children("form").transitionEndOrTimeout(function() { + + // Hide unlock display + self.unlockDisplay.addClass("hide"); + + // Reset + self.reset(); + + // Hide loading + self.hideLoading(); + + // Set that unlock display button isn't loading + self.unlockDisplay.find("form").children("button").removeClass("loading"); + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Check if focused element is clicked + if(focusedElement !== Focus.NO_FOCUS && focusedElement.hasClass("clicked") === true) + + // Set that focused element isn't clicked + focusedElement.removeClass("clicked"); + + // Clear unlock display shown input values + self.unlockDisplay.find("input:not(.hide)").val("").trigger("input"); + + // Unhide unlock display logo + self.unlockDisplay.children("div.logo").removeClass("hide"); + + // Set that unlock display can show status + self.unlockDisplay.addClass("showStatus"); + + // Set show install app to if hasn't been previously unlocked + var showInstallApp = self.unlockedAtLeastOnce === false; + + // Set unlocked at least once + self.unlockedAtLeastOnce = true; + + // Disable unlocked + self.unlocked.disable(); + + // Show unlocked display + self.unlockedDisplay.removeClass("hide"); + + // Set timeout + setTimeout(function() { + + // Trigger resize event + $(window).trigger("resize"); + + // Show unlocked display children + self.unlockedDisplay.children().removeClass("hide"); + + // Trigger section shown event on the current section + $(self.sections.getCurrentSection()).trigger(Section.SHOWN_EVENT); + + // Trigger unlocked show event + $(self.unlocked).trigger(Unlocked.SHOW_EVENT); + + // Check if message is not shown + if(self.message.isShown() === false) { + + // Unlocked display children transition end or timeout event + self.unlockedDisplay.children().transitionEndOrTimeout(function() { + + // Allow automatic lock + self.automaticLock.allow(); + + }, "opacity"); + + // Enable unlocked + self.unlocked.enable(); + + // Focus on unlocked display + self.unlockedDisplay.focus(); + + // Trigger section focus event on the current section + $(self.sections.getCurrentSection()).trigger(Section.FOCUS_EVENT); + } + + // Otherwise + else { + + // Unlocked display children transition end or timeout event + self.unlockedDisplay.children().transitionEndOrTimeout(function() { + + // Allow automatic lock + self.automaticLock.allow(); + + // Check if message is not shown + if(self.message.isShown() === false) { + + // Enable unlocked + self.unlocked.enable(); + + // Focus on unlocked display + self.unlockedDisplay.focus(); + + // Trigger section focus event on the current section + $(self.sections.getCurrentSection()).trigger(Section.FOCUS_EVENT); + } + }, "opacity"); + } + + // Allow showing messages + self.message.allow(); + + // Check if showing install app + if(showInstallApp === true) { + + // Set timeout + setTimeout(function() { + + // Show install app + self.installApp.show(); + + }, Common.randomNumber(Application.SHOW_INSTALL_APP_MINIMUM_DELAY_SECONDS, Application.SHOW_INSTALL_APP_MAXIMUM_DELAY_SECONDS) * Common.MILLISECONDS_IN_A_SECOND); + } + }, 0); + + }, "opacity"); + + }, Application.SHOW_UNLOCKED_DISPLAY_DELAY_MILLISECONDS); + + // Catch errors + }).catch(function(error) { + + // Reset unlocked + self.unlocked.reset(); + + // Lock wallets + self.wallets.lock(self.unlockedAtLeastOnce === false); + + // Show unlock error + showUnlockError(error); + }); + + // Catch errors + }).catch(function(error) { + + // Lock wallets + self.wallets.lock(self.unlockedAtLeastOnce === false); + + // Show unlock error + showUnlockError(error); + }); + } + }); + + // Unlock display delete all wallets button click event + this.unlockDisplay.find("div.deleteAllWallets").children().on("click", function(event) { + + // Get button + var button = $(this); + + // Show message + self.message.show(Language.getDefaultTranslation('Delete All Wallets'), Message.createText(Language.getDefaultTranslation('Are you sure you want to delete all your wallets?')) + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('Each wallet can only be recovered by using its passphrase or hardware wallet once it\'s been deleted.')) + "", false, function() { + + // Save focus and blur + self.focus.save(true); + + // Set that unlock display delete all wallets button is clicked + button.addClass("clicked"); + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + }, Language.getDefaultTranslation('No'), Language.getDefaultTranslation('Yes'), false, Message.VISIBLE_STATE_ALL, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if deleting all wallets + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Prevent showing messages + self.message.prevent(); + + // Show loading + self.showLoading(); + + // Set that message second button is loading + self.message.setButtonLoading(Message.SECOND_BUTTON); + + // Set timeout + setTimeout(function() { + + // Clear sections stack if applicable + var clearSectionsStackIfApplicable = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if wallet section is in the sections stack + if(self.sections.isSectionInStack(WalletSection.NAME) === true) { + + // Return clear sections stack + return self.sections.clearStack(true).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + }; + + // Clear sections stack if applicable + clearSectionsStackIfApplicable().then(function() { + + // Remove all wallets + self.wallets.removeAllWallets().then(function() { + + // Hide unlock display form + self.unlockDisplay.children("form").addClass("hide"); + + // Hide unlock display delete all wallets button + self.unlockDisplay.find("div.deleteAllWallets").addClass("hide"); + + // Hide message + self.message.hide(); + + // Unlock display delete all wallets button transition end or timeout event + self.unlockDisplay.find("div.deleteAllWallets").transitionEndOrTimeout(function() { + + // Hide unlock display + self.unlockDisplay.addClass("hide"); + + // Reset + self.reset(); + + // Hide loading + self.hideLoading(); + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Delete focus + self.focus.delete(); + + // Clear unlock display shown input values + self.unlockDisplay.find("input:not(.hide)").val("").trigger("input"); + + // Disable tabbing to everything in create display and disable everything in create display + self.createDisplay.find("*").disableTab().disable(); + + // Show create display + self.createDisplay.removeClass("hide"); + + // Set timeout + setTimeout(function() { + + // Show create display form + self.createDisplay.children("form").removeClass("hide"); + + // Enable tabbing to everything in create display and enable everything in create display + self.createDisplay.find("*").enableTab().enable(); + + // Focus on create display + self.createDisplay.focus(); + + // Allow showing messages + self.message.allow(); + }, 0); + + }, "opacity"); + + // Catch errors + }).catch(function(error) { + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Delete All Wallets Error'), Message.createText(error), true, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + }); + + // Catch errors + }).catch(function(error) { + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Delete All Wallets Error'), Message.createText(error), true, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + }); + }); + }, Application.DELETE_ALL_WALLETS_DELAY_MILLISECONDS); + } + + // Otherwise + else { + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Set that button isn't clicked + button.removeClass("clicked"); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide(); + } + } + }); + + // Unlock display delete all wallets button transition event + }).on("transitionend", function() { + + // Get button + var button = $(this); + + // Get button text + var buttonText = button.children(); + + // Check if button is expanded + if((((typeof matchMedia === "function" && matchMedia("(any-hover: hover)")["matches"] === true && button.is(":hover") === true) || button.is(":focus") === true) && button.is(":disabled") === false) || button.hasClass("clicked") === true) { + + // Get button max width + var buttonMaxWidth = parseFloat(button.css("font-size")) * Application.DELETE_ALL_WALLETS_BUTTON_MAXIMUM_WIDTH; + + // Check if button text isn't fully showing + if(button.outerWidth() >= button.parent().width() || buttonText.get(0)["scrollWidth"] >= buttonMaxWidth) { + + // Make button text truncate + buttonText.addClass("truncate"); + + // Check if button is larger than it's parent + if(buttonMaxWidth > button.parent().width()) + + // Make button text align left + buttonText.addClass("alignLeft"); + + // Otherwise + else + + // Make button text align center + buttonText.removeClass("alignLeft"); + } + + // Otherwise + else + + // Make button text not truncate and align center + buttonText.removeClass("truncate alignLeft"); + } + + // Otherwise + else + + // Make button text not truncate and align center + buttonText.removeClass("truncate alignLeft"); + }); + + // Unlock display delete all wallets button text language change event + this.unlockDisplay.find("div.deleteAllWallets").children().children().on(Language.CHANGE_EVENT, function() { + + // Trigger transition end event on parent + $(this).parent().trigger("transitionend"); + }); + } + + // Show create display + showCreateDisplay() { + + // Show create display + this.showDisplay(this.createDisplay); + } + + // Show lock display + showLockDisplay() { + + // Show unlock display + this.showDisplay(this.unlockDisplay); + } + + // Is create diplay shown + isCreateDisplayShown() { + + // Return if create display's children is shown + return this.createDisplay.children().hasClass("hide") === false; + } + + // Is unlock diplay shown + isUnlockDisplayShown() { + + // Return if unlock display's children is shown + return this.unlockDisplay.children().hasClass("hide") === false; + } + + // Is unlocked diplay shown + isUnlockedDisplayShown() { + + // Return if unlocked display's children is shown + return this.unlockedDisplay.children().hasClass("hide") === false; + } + + // Show loading + showLoading() { + + // Set that body display is loading + this.bodyDisplay.addClass("loading"); + } + + // Hide loading + hideLoading() { + + // Set that body display isn't loading + this.bodyDisplay.removeClass("loading"); + } + + // Is showing loading + isShowingLoading() { + + // Return if showing loading + return this.bodyDisplay.hasClass("loading") === true; + } + + // Show approve receiving payment message + showApproveReceivingPaymentMessage(wallet, slate, allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Prompt to approve + var promptToApprove = function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if preventing messages or messages are allowed and no message is shown + if(preventMessages === true || (self.message.getAllowed() === true && self.message.isShown() === false)) { + + // Initialize prevent cancel on hide + var preventCancelOnHide = false; + + // Initialize external cancel check allowed + var externalCancelCheckAllowed = true; + + // Initialize sleep disabled + var sleepDisabled = false; + + // Show message + self.message.show(Language.getDefaultTranslation('Approve Receiving Payment'), Message.createPendingResult() + Message.createLineBreak() + Message.createText((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('Do you approve receiving a payment for Wallet %1$s of %2$c with a fee of %3$c and %4$x kernel features?') : Language.getDefaultTranslation('Do you approve receiving a payment for %1$y of %2$c with a fee of %3$c and %4$x kernel features?'), [ + + // Wallet key path or name + (wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName(), + + [ + + // Number + slate.getAmount().dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + [ + + // Number + slate.getFee().dividedBy(Consensus.VALUE_NUMBER_BASE).toFixed(), + + // Currency + Consensus.CURRENCY_NAME, + + // Display value + true + ], + + // Kernel features + slate.getDisplayKernelFeatures() + + ]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + ((slate.getSenderAddress() !== Slate.NO_SENDER_ADDRESS) ? Message.createText(Language.getDefaultTranslation('The transaction\'s sender payment proof address is the following payment proof address.')) + Message.createLineBreak() + Message.createLineBreak() + "" + Common.htmlEncode(slate.getSenderAddress()) + "" + Language.createTranslatableContainer("", Language.getDefaultTranslation('Copy'), [], "copy", true) + "" + Message.createLineBreak() + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('You can guarantee that this payment is coming from the intended sender by having the sender confirm that this payment proof address is their payment proof address.')) + "" : (Message.createText(Language.getDefaultTranslation('The transaction doesn\'t have a payment proof.')) + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('You can\'t guarantee that this payment is coming from the intended sender since the transaction doesn\'t have a payment proof.')) + "")), preventMessages === true, function() { + + // Check if cancel didn't occur and wallet exists + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) && self.wallets.walletExists(wallet.getKeyPath()) === true) { + + // Check if preventing messages + if(preventMessages === true) { + + // Hide loading + self.hideLoading(); + } + + // Otherwise + else { + + // Save focus and blur + self.focus.save(true); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Keep device awake and catch errors + self.wakeLock.preventLock().catch(function(error) { + + }); + + // Set sleep disabled + sleepDisabled = true; + } + + // Cancel if external canceled + var cancelIfExternalCanceled = function() { + + // Check if external cancel check if allowed + if(externalCancelCheckAllowed === true) { + + // Check if cancel occurred + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED && cancelOccurred() === true) { + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise + else { + + // Check if sleep is disabled + if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + }); + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + } + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Cancel if external canceled + cancelIfExternalCanceled(); + + }, Application.CANCELED_CHECK_INTERVAL_MILLISECONDS); + } + } + }; + + // Cancel if external canceled + cancelIfExternalCanceled(); + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('No'), Language.getDefaultTranslation('Yes'), preventMessages === true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if receiving payment + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Replace message + self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + + // Otherwise + else { + + // Check if sleep is disabled + if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Replace message + self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Replace message + self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + } + } + + // Otherwise + else { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise + else { + + // Check if sleep is disabled + if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + }); + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Hide message + self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + } + } + } + + // Otherwise check if not preventing cancel on hide + else if(preventCancelOnHide === false) { + + // Check if sleep is disabled + if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Otherwise + else { + + // Check if a high priority wallets exclusive transactions lock is waiting + if(self.transactions.isHighPriorityWalletsExclusiveTransactionsLockWaiting(wallet.getKeyPath()) === true) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Prompt to approve + promptToApprove(); + + }, Application.CHECK_HARDWARE_WALLET_PRIORITY_INTERVAL_MILLISECONDS); + } + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }; + + // Prompt to approve + promptToApprove(); + }); + } + + // Show hardware wallet connect message + showHardwareWalletConnectMessage(wallet, text, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED, recursivelyShown = false) { + + // set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Prompt to connect + var promptToConnect = function() { + + // Check if cancel didn't occur or recursively shown + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || recursivelyShown === true) { + + // Check if preventing messages, recursively shown, or messages are allowed and no message is shown + if(preventMessages === true || recursivelyShown === true || (self.message.getAllowed() === true && self.message.isShown() === false)) { + + // Initialize prevent cancel on hide + var preventCancelOnHide = false; + + // Initialize external cancel check allowed + var externalCancelCheckAllowed = true; + + // Return showing message and do it immediately if preventing messages or recursively shown + return self.message.show(Language.getDefaultTranslation('Hardware Wallet Disconnected'), Message.createPendingResult() + Message.createLineBreak() + Message.createText(text, textArguments), preventMessages === true || recursivelyShown === true, function() { + + // Check if cancel didn't occur or recursively shown and wallet exists + if((cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || recursivelyShown === true) && self.wallets.walletExists(wallet.getKeyPath()) === true) { + + // Check if wallet's hardware wallet is connected + if(wallet.isHardwareConnected() === true) { + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Resolve + resolve(); + + // Return false + return false; + } + + // Otherwise + else { + + // Check if preventing messages + if(preventMessages === true) { + + // Hide loading + self.hideLoading(); + } + + // Otherwise + else { + + // Save focus and blur + self.focus.save(true); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + } + + // Document wallet connect application wallet key path event + $(document).on(Wallet.CONNECT_EVENT + ".application" + wallet.getKeyPath().toFixed(), function(event, walletKeyPath) { + + // Check if wallet's hardware wallet is connected + if(walletKeyPath === wallet.getKeyPath()) { + + // Turn off document wallet connect application wallet key path event + $(document).off(Wallet.CONNECT_EVENT + ".application" + wallet.getKeyPath().toFixed()); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + }); + + // Cancel if external canceled + var cancelIfExternalCanceled = function() { + + // Check if external cancel check if allowed + if(externalCancelCheckAllowed === true) { + + // Check if cancel occurred and not recursively shown + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED && cancelOccurred() === true && recursivelyShown === false) { + + // Turn off document wallet connect application wallet key path event + $(document).off(Wallet.CONNECT_EVENT + ".application" + wallet.getKeyPath().toFixed()); + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Cancel if external canceled + cancelIfExternalCanceled(); + + }, Application.CANCELED_CHECK_INTERVAL_MILLISECONDS); + } + } + }; + + // Cancel if external canceled + cancelIfExternalCanceled(); + } + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('Cancel'), Language.getDefaultTranslation('Connect'), preventMessages === true || recursivelyShown === true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off document wallet connect application wallet key path event + $(document).off(Wallet.CONNECT_EVENT + ".application" + wallet.getKeyPath().toFixed()); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if connect + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Return obtain wallets exclusive hardware lock + return self.wallets.obtainExclusiveHardwareLock().then(function() { + + // Check if wallet still isn't connected to a hardware wallet + if(wallet.isHardwareConnected() === false) { + + // Show hardware wallet error + var showHardwareWalletError = function(message) { + + // Disable message + self.message.disable(); + + // Return promise + return new Promise(function(resolve, reject) { + + // Return showing message immediately and allow showing messages + return self.message.show(Language.getDefaultTranslation('Hardware Wallet Error'), message, true, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Return showing hardware connect message immediately + return self.showHardwareWalletConnectMessage(wallet, text, textArguments, allowUnlock, preventMessages, cancelOccurred, true).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + }); + }; + + // Check if hardware wallets are supported + if(HardwareWallet.isSupported() === true) { + + // Get if automatic lock state + var automaticLockState = self.automaticLock.getAllowed(); + + // Prevent inactive automatic lock + self.automaticLock.prevent(); + + // Initialize canceled + var canceled = false; + + // Return showing message immediately and allow showing messages + return self.message.show(Language.getDefaultTranslation('Hardware Wallet Disconnected'), Message.createPendingResult() + Message.createLineBreak() + Message.createText(Language.getDefaultTranslation('Connecting to a hardware wallet.')), true, function() { + + // Message show application hardware wallet connect event + $(self.message).one(Message.SHOW_EVENT + ".applicationHardwareWalletConnect", function() { + + // Create hardware wallet + var hardwareWallet = new HardwareWallet(self); + + // Message before replace application hardware wallet connect event + $(self.message).on(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletConnect", function(event, messageType, messageData) { + + // Check if not canceled + if(canceled === false) { + + // Check message type + switch(messageType) { + + // Application hardware wallet unlock message + case Application.HARDWARE_WALLET_UNLOCK_MESSAGE: + + // Cancel replacing message + self.message.cancelReplace(); + + // Return false to stop other replace message + return false; + + // Application hardware wallet disconnect message + case Application.HARDWARE_WALLET_DISCONNECT_MESSAGE: + + // Set canceled + canceled = true; + + // Turn off message before replace application hardware wallet connect event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletConnect"); + + // Restore automatic lock state + self.automaticLock.allow(automaticLockState); + + // Check if automatic lock is locking and message doesn't allow unlock + if(self.automaticLock.isLocking() === true && allowUnlock === false) { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Show hardware wallet error + showHardwareWalletError(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Return false to stop other replace message + return false; + } + } + }); + + // Return connecting to any hardware wallet descriptor + return hardwareWallet.connect(HardwareWallet.ANY_HARDWARE_WALLET_DESCRIPTOR, false, Language.getDefaultTranslation('Unlock that hardware wallet to continue connecting to it.'), [], allowUnlock, true, cancelOccurred).then(function() { + + // Check if not canceled + if(canceled === false) { + + // Turn off message before replace application hardware wallet connect event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletConnect"); + + // Restore automatic lock state + self.automaticLock.allow(automaticLockState); + + // Check if automatic lock is locking and message doesn't allow unlock + if(self.automaticLock.isLocking() === true && allowUnlock === false) { + + // Close the hardware wallet + hardwareWallet.close(); + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + + // Disable message + self.message.disable(); + + // Return connecting wallet to the applicable hardware wallet + return wallet.connectToApplicableHardware([hardwareWallet]).then(function() { + + // Check if hardware wallet isn't in use + if(hardwareWallet.getInUse() === false) { + + // Close hardware wallet + hardwareWallet.close(); + + // Return showing hardware wallet error + return showHardwareWalletError(Message.createText((wallet.getName() === Wallet.NO_NAME) ? Language.getDefaultTranslation('That hardware wallet isn\'t for Wallet %1$s.') : Language.getDefaultTranslation('That hardware wallet isn\'t for %1$y.'), [(wallet.getName() === Wallet.NO_NAME) ? wallet.getKeyPath().toFixed() : wallet.getName()])).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + + // Catch errors + }).catch(function(error) { + + // Close the hardware wallet + hardwareWallet.close(); + + // Return showing hardware wallet error + return showHardwareWalletError(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.'))).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Otherwise + else { + + // Close the hardware wallet + hardwareWallet.close(); + } + + // Catch errors + }).catch(function(error) { + + // Check if not canceled + if(canceled === false) { + + // Turn off message before replace application hardware wallet connect event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletConnect"); + + // Restore automatic lock state + self.automaticLock.allow(automaticLockState); + + // Check if automatic lock is locking and message doesn't allow unlock + if(self.automaticLock.isLocking() === true && allowUnlock === false) { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + + // Check if error is canceled + if(error === Common.CANCELED_ERROR) { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise + else { + + // Return showing hardware wallet error + return showHardwareWalletError(error).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + } + }); + }); + + }, Language.getDefaultTranslation('Back'), Message.NO_BUTTON, true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off message show application hardware wallet connect event + $(self.message).off(Message.SHOW_EVENT + ".applicationHardwareWalletConnect"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if not canceled + if(canceled === false) { + + // Set canceled + canceled = true; + + // Turn off message before replace application hardware wallet connect event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletConnect"); + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Restore automatic lock state + self.automaticLock.allow(automaticLockState); + + // Check if automatic lock is locking and message doesn't allow unlock + if(self.automaticLock.isLocking() === true && allowUnlock === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + + // Return showing hardware connect message immediately + return self.showHardwareWalletConnectMessage(wallet, text, textArguments, allowUnlock, preventMessages, cancelOccurred, true).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + } + }); + } + + // Otherwise + else { + + // Return showing hardware wallet error + return showHardwareWalletError(Message.createText(Language.getDefaultTranslation('Your browser doesn\'t allow using USB or Bluetooth devices.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Update your browser to use this feature.'))).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + } + + // Otherwise + else { + + // Release wallets exclusive hardware lock + self.wallets.releaseExclusiveHardwareLock(); + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + }); + } + + // Otherwise + else { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + } + + // Otherwise check if not preventing cancel on hide + else if(preventCancelOnHide === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if a high priority wallets exclusive transactions lock is waiting + if(self.transactions.isHighPriorityWalletsExclusiveTransactionsLockWaiting(wallet.getKeyPath()) === true) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Prompt to connect + promptToConnect(); + + }, Application.CHECK_HARDWARE_WALLET_PRIORITY_INTERVAL_MILLISECONDS); + } + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }; + + // Prompt to connect + promptToConnect(); + }); + } + + // Show hardware wallet unlock message + showHardwareWalletUnlockMessage(hardwareWallet, text, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Prompt to unlock + var promptToUnlock = function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if preventing messages or messages are allowed and no message is shown + if(preventMessages === true || (self.message.getAllowed() === true && self.message.isShown() === false)) { + + // Initialize prevent cancel on hide + var preventCancelOnHide = false; + + // Initialize external cancel check allowed + var externalCancelCheckAllowed = true; + + // Check hardware wallet's transport type + switch(hardwareWallet.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Set message + var message = Message.createPendingResult() + Message.createLineBreak() + Message.createText(text, textArguments); + + // Set second button + var secondButton = Message.NO_BUTTON; + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check hardware wallet's transport product name + switch(hardwareWallet.transport["deviceModel"]["productName"]) { + + // Trezor Model One + case "Trezor Model One": + + // Set message + var message = Message.createText(text, textArguments) + Message.createLineBreak() + "" + Message.createText(Language.getDefaultTranslation('Enter your pin as the following alphabetic characters to unlock the hardware wallet.')) + "" + Message.createLineBreak() + Message.createPinMatrix() + Message.createLineBreak() + Message.createLineBreak() + Message.createInput(Language.getDefaultTranslation('Pin'), [], false) + Message.createLineBreak(); + + // Set second button + var secondButton = Language.getDefaultTranslation('Unlock'); + + // Break + break; + + // Trezor Model T, Trezor Safe 3, Trezor Safe 5, or default + case "Trezor Model T": + case "Trezor Safe 3": + case "Trezor Safe 5": + default: + + // Set message + var message = Message.createPendingResult() + Message.createLineBreak() + Message.createText(text, textArguments); + + // Set second button + var secondButton = Message.NO_BUTTON; + + // Break + break; + } + + // Break + break; + } + + // Return showing message and do it immediately if preventing messages + return self.message.show(Language.getDefaultTranslation('Hardware Wallet Locked'), message, preventMessages === true, function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if hardware wallet is connected and locked + if(hardwareWallet.isConnected() === true && hardwareWallet.isLocked() === true) { + + // Check if preventing messages + if(preventMessages === true) { + + // Hide loading + self.hideLoading(); + } + + // Otherwise + else { + + // Save focus and blur + self.focus.save(true); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + } + + // Hardware wallet unlock application event + $(hardwareWallet).one(HardwareWallet.UNLOCK_EVENT + ".application", function(event) { + + // Turn off hardware wallet disconnect application event + $(hardwareWallet).off(HardwareWallet.DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet before disconnect application event + $(hardwareWallet).off(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet device cancel application event + $(hardwareWallet).off(HardwareWallet.DEVICE_CANCEL_EVENT + ".application"); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace(Application.HARDWARE_WALLET_UNLOCK_MESSAGE).then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + + // Hardware wallet disconnect application event + $(hardwareWallet).one(HardwareWallet.DISCONNECT_EVENT + ".application", function(event) { + + // Turn off hardware wallet unlock application event + $(hardwareWallet).off(HardwareWallet.UNLOCK_EVENT + ".application"); + + // Turn off hardware wallet before disconnect application event + $(hardwareWallet).off(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet device cancel application event + $(hardwareWallet).off(HardwareWallet.DEVICE_CANCEL_EVENT + ".application"); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace(Application.HARDWARE_WALLET_DISCONNECT_MESSAGE).then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + + // Hardware wallet before disconnect application event + $(hardwareWallet).one(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application", function(event) { + + // Turn off hardware wallet unlock application event + $(hardwareWallet).off(HardwareWallet.UNLOCK_EVENT + ".application"); + + // Turn off hardware wallet disconnect application event + $(hardwareWallet).off(HardwareWallet.DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet device cancel application event + $(hardwareWallet).off(HardwareWallet.DEVICE_CANCEL_EVENT + ".application"); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Return replacing message + return self.message.replace(Application.HARDWARE_WALLET_DISCONNECT_MESSAGE).then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + + // Hardware wallet device cancel application event + $(hardwareWallet).one(HardwareWallet.DEVICE_CANCEL_EVENT + ".application", function(event) { + + // Turn off hardware wallet unlock application event + $(hardwareWallet).off(HardwareWallet.UNLOCK_EVENT + ".application"); + + // Turn off hardware wallet disconnect application event + $(hardwareWallet).off(HardwareWallet.DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet before disconnect application event + $(hardwareWallet).off(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application"); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + }); + + // Cancel if external canceled + var cancelIfExternalCanceled = function() { + + // Check if external cancel check if allowed + if(externalCancelCheckAllowed === true) { + + // Check if cancel occurred + if(cancelOccurred !== Common.NO_CANCEL_OCCURRED && cancelOccurred() === true) { + + // Turn off hardware wallet unlock application event + $(hardwareWallet).off(HardwareWallet.UNLOCK_EVENT + ".application"); + + // Turn off hardware wallet disconnect application event + $(hardwareWallet).off(HardwareWallet.DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet before disconnect application event + $(hardwareWallet).off(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet device cancel application event + $(hardwareWallet).off(HardwareWallet.DEVICE_CANCEL_EVENT + ".application"); + + // Disable message + self.message.disable(); + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Cancel if external canceled + cancelIfExternalCanceled(); + + }, Application.CANCELED_CHECK_INTERVAL_MILLISECONDS); + } + } + }; + + // Cancel if external canceled + cancelIfExternalCanceled(); + } + + // Otherwise + else { + + // Set prevent cancel on hide + preventCancelOnHide = true; + + // Resolve + resolve(); + + // Return false + return false; + } + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('Cancel'), secondButton, preventMessages === true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off hardware wallet unlock application event + $(hardwareWallet).off(HardwareWallet.UNLOCK_EVENT + ".application"); + + // Turn off hardware wallet disconnect application event + $(hardwareWallet).off(HardwareWallet.DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet before disconnect application event + $(hardwareWallet).off(HardwareWallet.BEFORE_DISCONNECT_EVENT + ".application"); + + // Turn off hardware wallet device cancel application event + $(hardwareWallet).off(HardwareWallet.DEVICE_CANCEL_EVENT + ".application"); + + // Clear external cancel check allowed + externalCancelCheckAllowed = false; + + // Check if canceling + if(messageResult === Message.FIRST_BUTTON_CLICKED_RESULT) { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Otherwise check if second button was clicked + else if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Try + try { + + // Get alphabetic pin + var alphabeticPin = self.message.getInputText().trim(); + } + + // Catch errors + catch(error) { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Prevent showing messages + self.message.prevent(); + } + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Return hiding message + return self.message.hide().then(function() { + + // Reject + reject(); + }); + } + + // Return + return; + } + + // Show loading + self.showLoading(); + + // Set that message second button is loading + self.message.setButtonLoading(Message.SECOND_BUTTON); + + // Disable message + self.message.disable(); + + // Resolve alphabetic pin + resolve(alphabeticPin); + } + + // Otherwise check if not preventing cancel on hide + else if(preventCancelOnHide === false) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if a high priority wallets exclusive transactions lock is waiting + if(self.transactions.isHighPriorityWalletsExclusiveTransactionsLockWaiting(hardwareWallet.getWalletKeyPath()) === true) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Prompt to unlock + promptToUnlock(); + + }, Application.CHECK_HARDWARE_WALLET_PRIORITY_INTERVAL_MILLISECONDS); + } + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }; + + // Prompt to unlock + promptToUnlock(); + }); + } + + // Hardware wallet unlock message done + hardwareWalletUnlockMessageDone(preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED, replaceMessage = true) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if preventing messages + if(preventMessages === true) { + + // Check if can't be canceled + if(cancelOccurred === Common.NO_CANCEL_OCCURRED) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + + // Otherwise check if replacing message + else if(replaceMessage === true) { + + // Hide loading + self.hideLoading(); + } + } + + // Otherwise + else { + + // Check if replacing message + if(replaceMessage === true) { + + // Hide loading + self.hideLoading(); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + } + + // Check if preventing messages and it can be canceled + if(preventMessages === true && cancelOccurred !== Common.NO_CANCEL_OCCURRED) { + + // Check if replacing message + if(replaceMessage === true) { + + // Return replacing message + return self.message.replace(Application.HARDWARE_WALLET_UNLOCK_MESSAGE).then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Check if replacing message + if(replaceMessage === true) { + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + }); + } + + // Show hardware wallet pending message + showHardwareWalletPendingMessage(hardwareWallet, text, allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED, recursivelyShown = false, rootCanceled = undefined) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Prompt to approve + var promptToApprove = function() { + + // Check if cancel didn't occur or recursively shown + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || recursivelyShown === true) { + + // Check if preventing messages, recursively shown, or messages are allowed and no message is shown + if(preventMessages === true || recursivelyShown === true || (self.message.getAllowed() === true && self.message.isShown() === false)) { + + // Initialize canceled + var canceled = { + + // Value + "Value": false + }; + + // Initialize sleep disabled + var sleepDisabled = false; + + // Return showing message and do it immediately if preventing messages or recursively shown + return self.message.show(Language.getDefaultTranslation('Hardware Wallet Approval Requested'), text, preventMessages === true || recursivelyShown === true, function() { + + // Check if cancel didn't occur or recursively shown + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || recursivelyShown === true) { + + // Check if hardware wallet is connected + if(hardwareWallet.isConnected() === true) { + + // Check if preventing messages + if(preventMessages === true) { + + // Hide loading + self.hideLoading(); + } + + // Otherwise + else { + + // Save focus and blur + self.focus.save(true); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Disable tabbing to everything in unlock display and disable everything in unlock display + self.unlockDisplay.find("*").disableTab().disable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Disable unlocked + self.unlocked.disable(); + + // Keep device awake and catch errors + self.wakeLock.preventLock().catch(function(error) { + + }); + + // Set sleep disabled + sleepDisabled = true; + } + + // Message before replace application hardware wallet approve event + $(self.message).on(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletApprove", function(event, messageType, messageData) { + + // Check if message type is hardware wallet unlock message + if(messageType === Application.HARDWARE_WALLET_UNLOCK_MESSAGE) { + + // Turn off message before replace application hardware wallet approve event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletApprove"); + + // Show hardware wallet pending message and catch errors + self.showHardwareWalletPendingMessage(hardwareWallet, text, allowUnlock, preventMessages, cancelOccurred, true, (recursivelyShown === true) ? rootCanceled : canceled).catch(function(error) { + + // Replace message + self.message.replace(Application.HARDWARE_WALLET_DISCONNECT_MESSAGE); + }); + + // Return false to stop other replace message + return false; + } + }); + + // Message show application hardware wallet approve event + $(self.message).one(Message.SHOW_EVENT + ".applicationHardwareWalletApprove", function() { + + // Resolve canceled + resolve(function() { + + // Return if canceled + return canceled["Value"] === true; + }); + }); + } + + // Otherwise + else { + + // Reject hardware wallet disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + + // Return false + return false; + } + } + + // Otherwise + else { + + // Return false + return false; + } + + }, Language.getDefaultTranslation('Cancel'), Message.NO_BUTTON, preventMessages === true || recursivelyShown === true, (allowUnlock === true) ? Message.VISIBLE_STATE_UNLOCK | Message.VISIBLE_STATE_UNLOCKED : Message.VISIBLE_STATE_UNLOCKED).then(function(messageResult) { + + // Turn off message show application hardware wallet approve event + $(self.message).off(Message.SHOW_EVENT + ".applicationHardwareWalletApprove"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Turn off message before replace application hardware wallet approve event + $(self.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletApprove"); + + // Check if recursively shown + if(recursivelyShown === true) { + + // Set root canceled + rootCanceled["Value"] = true; + } + + // Otherwise + else { + + // Set canceled + canceled["Value"] = true; + } + + // Check if sleep is disabled + if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + + // Hide message + self.message.hide().then(function() { + + // Replace message + self.message.replace(Application.HARDWARE_WALLET_DISCONNECT_MESSAGE); + }); + }); + } + + // Otherwise + else { + + // Check if preventing messages + if(preventMessages === true) { + + // Prevent showing messages + self.message.prevent(); + } + + // Otherwise + else { + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Hide message + self.message.hide().then(function() { + + // Replace message + self.message.replace(Application.HARDWARE_WALLET_DISCONNECT_MESSAGE); + }); + } + } + + // Otherwise check if sleep is disabled + else if(sleepDisabled === true) { + + // Allow device to sleep and catch errors + self.wakeLock.allowLock().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Check if a high priority wallets exclusive transactions lock is waiting + if(self.transactions.isHighPriorityWalletsExclusiveTransactionsLockWaiting(hardwareWallet.getWalletKeyPath()) === true) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Set timeout + setTimeout(function() { + + // Prompt to approve + promptToApprove(); + + }, Application.CHECK_HARDWARE_WALLET_PRIORITY_INTERVAL_MILLISECONDS); + } + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }; + + // Prompt to approve + promptToApprove(); + }); + } + + // Hardware wallet pending message done + hardwareWalletPendingMessageDone(preventMessages = false) { + + // Turn off message before replace application hardware wallet approve event + $(this.message).off(Message.BEFORE_REPLACE_EVENT + ".applicationHardwareWalletApprove"); + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if preventing messages + if(preventMessages === true) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + } + + // Otherwise + else { + + // Hide loading + self.hideLoading(); + + // Check if unlock display is shown + if(self.isUnlockDisplayShown() === true) + + // Enable tabbing to everything in unlock display and enable everything in unlock display + self.unlockDisplay.find("*").enableTab().enable(); + + // Otherwise check if unlocked display is shown + else if(self.isUnlockedDisplayShown() === true) + + // Enable unlocked + self.unlocked.enable(); + + // Restore focus and don't blur + self.focus.restore(false); + } + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + }); + } + + // Is disabled + isDisabled() { + + // Check if create display is shown + if(this.isCreateDisplayShown() === true) { + + // Return if create display is disabled + return this.createDisplay.children("div").first().attr("tabindex") === Common.NO_TAB_INDEX; + } + + // Otherwise check if unlock display is shown + else if(this.isUnlockDisplayShown() === true) { + + // Return if unlock display is disabled + return this.unlockDisplay.children("div").first().attr("tabindex") === Common.NO_TAB_INDEX; + } + + // Otherwise + else { + + // Return false + return false; + } + } + + // Show loading delay milliseconds + static get SHOW_LOADING_DELAY_MILLISECONDS() { + + // Return show loading delay milliseconds + return 100; + } + + // Hardware wallet disconnect message + static get HARDWARE_WALLET_DISCONNECT_MESSAGE() { + + // Return hardware wallet disconnect message + return "ApplicationHardwareWalletDisconnectMessage"; + } + + // Hardware wallet unlock message + static get HARDWARE_WALLET_UNLOCK_MESSAGE() { + + // Return hardware wallet unlock message + return "ApplicationHardwareWalletUnlockMessage"; + } + + // Private + + // Is not iframe + isNotIframe() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if not an iframe + if(Common.isIframe() === false) + + // Resolve + resolve(); + + // Otherwise + else { + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('This extension won\'t run when it\'s embedded in a site.')); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('This app won\'t run when it\'s embedded in a site.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('This site won\'t run when it\'s embedded in a site. Visit %1$l to continue.'), [ + + [ + // Text + Language.getDefaultTranslation('MWC Wallet'), + + // URL + location["href"], + + // Is external + true, + + // Is blob + false + ] + ]); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + } + }); + } + + // Browser is compatible + browserIsCompatible() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Try + try { + + // Check if math isn't supported + if(typeof Math !== "object" || Math === null) + + // Throw error + throw "Math isn't supported."; + + // Otherwise check if document isn't supported + else if(typeof document !== "object" || document === null) + + // Throw error + throw "Document isn't supported."; + + // Otherwise check if window isn't supported + else if(typeof window !== "object" || window === null) + + // Throw error + throw "Window isn't supported."; + + // Otherwise check if global this isn't supported + else if(typeof globalThis !== "object" || globalThis === null) + + // Throw error + throw "Global this isn't supported."; + + // Otherwise Check if promises aren't supported + else if(typeof Promise !== "function") + + // Throw error + throw "Promises aren't supported."; + + // Otherwise check if crypto isn't supported + else if(typeof crypto !== "object" || crypto === null) + + // Throw error + throw "Crypto isn't supported."; + + // Otherwise check if IndexedDB isn't supported + else if(typeof indexedDB !== "object" || indexedDB === null) + + // Throw error + throw "IndexedDB isn't supported."; + + // Otherwise check if IDBKeyRange isn't supported + else if(typeof IDBKeyRange !== "function") + + // Throw error + throw "IDBKeyRange isn't supported."; + + // Otherwise check if Number isn't supported + else if(typeof Number !== "function") + + // Throw error + throw "Number isn't supported."; + + // Otherwise check if String isn't supported + else if(typeof String !== "function") + + // Throw error + throw "String isn't supported."; + + // Otherwise check if local storage isn't supported + else if(typeof localStorage !== "object" || localStorage === null) + + // Throw error + throw "Local storage isn't supported."; + + // Otherwise check if 8-bit unsigned integer arrays aren't supported + else if(typeof Uint8Array !== "function") + + // Throw error + throw "8-bit unsigned integer arrays aren't supported."; + + // Otherwise check if 16-bit unsigned integer arrays aren't supported + else if(typeof Uint16Array !== "function") + + // Throw error + throw "16-bit unsigned integer arrays aren't supported."; + + // Otherwise check if 8-bit unsigned integer clamped arrays aren't supported + else if(typeof Uint8ClampedArray !== "function") + + // Throw error + throw "8-bit unsigned integer clamped arrays aren't supported."; + + // Otherwise check if text encoders aren't supported + else if(typeof TextEncoder !== "function") + + // Throw error + throw "Text encoders aren't supported."; + + // Otherwise check if text decoders aren't supported + else if(typeof TextDecoder !== "function") + + // Throw error + throw "Text decoders aren't supported."; + + // Otherwise check if URLs aren't supported + else if(typeof URL !== "function") + + // Throw error + throw "URLs aren't supported."; + + // Otherwise check if responses aren't supported + else if(typeof Response !== "function") + + // Throw error + throw "Responses aren't supported."; + + // Otherwise check if headers aren't supported + else if(typeof Headers !== "function") + + // Throw error + throw "Headers aren't supported."; + + // Otherwise check if data views aren't supported + else if(typeof DataView !== "function") + + // Throw error + throw "Data views aren't supported."; + + // Otherwise check if 32-bit unsigned integer arrays aren't supported + else if(typeof Uint32Array !== "function") + + // Throw error + throw "32-bit unsigned integer arrays aren't supported."; + + // Otherwise check if array buffers aren't supported + else if(typeof ArrayBuffer !== "function") + + // Throw error + throw "Array buffers aren't supported."; + + // Otherwise check if arrays aren't supported + else if(typeof Array !== "function") + + // Throw error + throw "Arrays aren't supported."; + + // Otherwise check if JSON isn't supported + else if(typeof JSON !== "object" || JSON === null) + + // Throw error + throw "JSON isn't supported."; + + // Otherwise check if WebSockets aren't supported + else if(typeof WebSocket !== "function") + + // Throw error + throw "WebSockets aren't supported."; + + // Otherwise check if Object isn't supported + else if(typeof Object !== "function") + + // Throw error + throw "Object isn't supported."; + + // Otherwise check if Date isn't supported + else if(typeof Date !== "function") + + // Throw error + throw "Date isn't supported."; + + // Otherwise check if Intl isn't supported + else if(typeof Intl !== "object" || Intl === null) + + // Throw error + throw "Intl isn't supported."; + + // Otherwise check if Sets aren't supported + else if(typeof Set !== "function") + + // Throw error + throw "Sets aren't supported."; + + // Otherwise check if WebAssembly isn't supported + else if(typeof WebAssembly !== "object" || WebAssembly === null) + + // Throw error + throw "WebAssembly isn't supported."; + + // Otherwise check if Web Workers aren't supported + else if(typeof Worker !== "function") + + // Throw error + throw "Web Workers aren't supported."; + + // Otherwise check if RegExp isn't supported + else if(typeof RegExp !== "function") + + // Throw error + throw "RegExp isn't supported."; + + // Otherwise check if 32-bit floating point arrays aren't supported + else if(typeof Float32Array !== "function") + + // Throw error + throw "32-bit floating point arrays aren't supported."; + + // Otherwise check if 64-bit floating point arrays aren't supported + else if(typeof Float64Array !== "function") + + // Throw error + throw "64-bit floating point arrays aren't supported."; + + // Otherwise check if Image isn't supported + else if(typeof Image !== "function") + + // Throw error + throw "Image isn't supported."; + + // Otherwise check if 16-bit signed integer arrays aren't supported + else if(typeof Int16Array !== "function") + + // Throw error + throw "16-bit signed integer arrays aren't supported."; + + // Otherwise check if 32-bit signed integer arrays aren't supported + else if(typeof Int32Array !== "function") + + // Throw error + throw "32-bit signed integer arrays aren't supported."; + + // Otherwise check if 8-bit signed integer arrays aren't supported + else if(typeof Int8Array !== "function") + + // Throw error + throw "8-bit signed integer arrays aren't supported."; + + // Otherwise check if RangeError isn't supported + else if(typeof RangeError !== "function") + + // Throw error + throw "RangeError isn't supported."; + + // Otherwise check if TypeError isn't supported + else if(typeof TypeError !== "function") + + // Throw error + throw "TypeError isn't supported."; + + // Otherwise check if Error isn't supported + else if(typeof Error !== "function") + + // Throw error + throw "Error isn't supported."; + + // Otherwise check if XMLHttpRequest isn't supported + else if(typeof XMLHttpRequest !== "function") + + // Throw error + throw "XMLHttpRequest isn't supported."; + + // Otherwise check if fetch isn't supported + else if(typeof fetch !== "function") + + // Throw error + throw "Fetch isn't supported."; + + // Otherwise check if set interval isn't supported + else if(typeof setInterval !== "function") + + // Throw error + throw "Set interval isn't supported."; + + // Otherwise check if clear interval isn't supported + else if(typeof clearInterval !== "function") + + // Throw error + throw "Clear interval isn't supported."; + + // Otherwise check if set timeout isn't supported + else if(typeof setTimeout !== "function") + + // Throw error + throw "Set timeout isn't supported."; + + // Otherwise check if clear timeout isn't supported + else if(typeof clearTimeout !== "function") + + // Throw error + throw "Clear timeout isn't supported."; + + // Otherwise check if request animation frame isn't supported + else if(typeof requestAnimationFrame !== "function") + + // Throw error + throw "Request animation frame isn't supported."; + + // Otherwise check if parse integer isn't supported + else if(typeof parseInt !== "function") + + // Throw error + throw "Parse integer isn't supported."; + + // Otherwise check if parse float isn't supported + else if(typeof parseFloat !== "function") + + // Throw error + throw "Parse float isn't supported."; + + // Otherwise check if decode URI component isn't supported + else if(typeof decodeURIComponent !== "function") + + // Throw error + throw "Decode URI component isn't supported."; + + // Otherwise check if encode URI component isn't supported + else if(typeof encodeURIComponent !== "function") + + // Throw error + throw "Encode URI component isn't supported."; + + // Otherwise check if Event isn't supported + else if(typeof Event !== "function") + + // Throw error + throw "Event isn't supported."; + + // Otherwise check if Function isn't supported + else if(typeof Function !== "function") + + // Throw error + throw "Function isn't supported."; + + // Otherwise check if DOM exception isn't supported + else if(typeof DOMException !== "function") + + // Throw error + throw "DOM exception isn't supported."; + + // Otherwise check if file reader isn't supported + else if(typeof FileReader !== "function") + + // Throw error + throw "File reader isn't supported."; + + // Otherwise check if Blobs aren't supported + else if(typeof Blob !== "function") + + // Throw error + throw "Blobs aren't supported."; + + // Otherwise + else + + // Resolve + resolve(); + } + + // Catch errors + catch(error) { + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), Message.createText(Language.getDefaultTranslation('Your browser isn\'t compatible. Update your browser to continue.')), false, function() { + + // Hide loading + self.hideLoading(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + } + }); + } + + // Install service worker + installServiceWorker() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check service work installer's installation status + switch(self.serviceWorkerInstaller.getInstallationStatus()) { + + // Installing + case ServiceWorkerInstaller.INSTALLING_STATUS: + + // Service worker installer install succeeded application event + $(self.serviceWorkerInstaller).one(ServiceWorkerInstaller.INSTALL_SUCCEEDED_EVENT + ".application", function() { + + // Turn off service worker installer install failed application event + $(self.serviceWorkerInstaller).off(ServiceWorkerInstaller.INSTALL_FAILED_EVENT + ".application"); + + // Resolve + resolve(); + + // Service worker installer install failed application event + }).one(ServiceWorkerInstaller.INSTALL_FAILED_EVENT + ".application", function() { + + // Turn off service worker installer install succeeded application event + $(self.serviceWorkerInstaller).off(ServiceWorkerInstaller.INSTALL_SUCCEEDED_EVENT + ".application"); + + // Check if is an app + if(Common.isApp() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to install or update the service worker.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this app to try again.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to install or update the service worker.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Refresh this site to try again.')); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + }); + + // Break + break; + + // Unsupported or installed + case ServiceWorkerInstaller.UNSUPPORTED_STATUS: + case ServiceWorkerInstaller.INSTALLED_STATUS: + + // Resolve + resolve(); + + // Break + break; + + // Failed + case ServiceWorkerInstaller.FAILED_STATUS: + + // Check if is an app + if(Common.isApp() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to install or update the service worker.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this app to try again.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to install or update the service worker.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Refresh this site to try again.')); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + + // Break + break; + } + }); + } + + // Show private browsing message + showPrivateBrowsingMessage() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Get if private browsing message has been shown + var privateBrowsingMessageShown = localStorage.getItem(Application.PRIVATE_BROWSING_MESSAGE_SHOWN_LOCAL_STORAGE_NAME); + + // Check if private browsing message hasn't been shown and not an app or extension + if((privateBrowsingMessageShown === Common.INVALID_LOCAL_STORAGE_ITEM || privateBrowsingMessageShown !== Application.PRIVATE_BROWSING_MESSAGE_SHOWN_TRUE_VALUE) && Common.isApp() === false && Common.isExtension() === false) { + + // Initialize message button clicked + var messageButtonClicked = false; + + // Window storage application event + $(window).on("storage.application", function(event) { + + // Check if private browsing message shown was changed + if(event["originalEvent"]["key"] === Application.PRIVATE_BROWSING_MESSAGE_SHOWN_LOCAL_STORAGE_NAME) { + + // Turn off window storage application event + $(window).off("storage.application"); + + // Turn off window resize application event + $(window).off("resize.application"); + + // Set timeout + setTimeout(function() { + + // Check if message button wasn't clicked + if(messageButtonClicked === false) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + + }, Application.HIDE_PRIVATE_BROWSING_MESSAGE_DELAY_MILLISECONDS); + } + }); + + // Windows resize application event + $(window).on("resize.application", function() { + + // Check if is an app + if(Common.isApp() === true) { + + // Turn off window storage application event + $(window).off("storage.application"); + + // Turn off window resize application event + $(window).off("resize.application"); + + // Set timeout + setTimeout(function() { + + // Check if message button wasn't clicked + if(messageButtonClicked === false) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + + }, Application.HIDE_PRIVATE_BROWSING_MESSAGE_DELAY_MILLISECONDS); + } + }); + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Private Browsing And Site Data Information'), Message.createText(Language.getDefaultTranslation('This site won\'t function correctly if you have private or incognito browsing modes enabled or if your browser is configured to automatically delete cookies and site data. Make sure that private and incognito browsing modes are disabled and that your browser is configured to retain cookies and site data before continuing.')), false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Turn off window storage application event + $(window).off("storage.application"); + + // Turn off window resize application event + $(window).off("resize.application"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Set message button clicked + messageButtonClicked = true; + + // Try + try { + + // Save that private browsing message has been shown + localStorage.setItem(Application.PRIVATE_BROWSING_MESSAGE_SHOWN_LOCAL_STORAGE_NAME, Application.PRIVATE_BROWSING_MESSAGE_SHOWN_TRUE_VALUE); + } + + // Catch errors + catch(error) { + + // Trigger a fatal error + new FatalError(FatalError.LOCAL_STORAGE_ERROR); + + // Return + return; + } + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + } + + // Otherwise + else + + // Resolve + resolve(); + }); + } + + // Show third-party cookies message + showThirdPartyCookiesMessage() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Get if third-party cookies message has been shown + var thirdPartyCookiesMessageShown = localStorage.getItem(Application.THIRD_PARTY_COOKIES_MESSAGE_SHOWN_LOCAL_STORAGE_NAME); + + // Check if third-party cookies message hasn't been shown and is an extension or loading from a file + if((thirdPartyCookiesMessageShown === Common.INVALID_LOCAL_STORAGE_ITEM || thirdPartyCookiesMessageShown !== Application.THIRD_PARTY_COOKIES_MESSAGE_SHOWN_TRUE_VALUE) && (Common.isExtension() === true || location["protocol"] === Common.FILE_PROTOCOL)) { + + // Check if browser is Safari + if(typeof navigator === "object" && navigator !== null && "userAgent" in navigator === true && navigator["userAgent"].toLowerCase().indexOf("safari") !== Common.INDEX_NOT_FOUND && navigator["userAgent"].toLowerCase().indexOf("chrome") === Common.INDEX_NOT_FOUND) { + + // Initialize message button clicked + var messageButtonClicked = false; + + // Window storage application event + $(window).on("storage.application", function(event) { + + // Check if third-party cookies message shown was changed + if(event["originalEvent"]["key"] === Application.THIRD_PARTY_COOKIES_MESSAGE_SHOWN_LOCAL_STORAGE_NAME) { + + // Turn off window storage application event + $(window).off("storage.application"); + + // Set timeout + setTimeout(function() { + + // Check if message button wasn't cicked + if(messageButtonClicked === false) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + + }, Application.HIDE_THIRD_PARTY_COOKIES_MESSAGE_DELAY_MILLISECONDS); + } + }); + + // Check if is an extesnion + if(Common.isExtension() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('This extension won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('This site won\'t function correctly if your browser is configured to block third-party cookies. Make sure that your browser is configured to allow third-party cookies before continuing.')); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Third-Party Cookies Information'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Turn off window storage application event + $(window).off("storage.application"); + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Set message button clicked + messageButtonClicked = true; + + // Try + try { + + // Save that third-party cookies message has been shown + localStorage.setItem(Application.THIRD_PARTY_COOKIES_MESSAGE_SHOWN_LOCAL_STORAGE_NAME, Application.THIRD_PARTY_COOKIES_MESSAGE_SHOWN_TRUE_VALUE); + } + + // Catch errors + catch(error) { + + // Trigger a fatal error + new FatalError(FatalError.LOCAL_STORAGE_ERROR); + + // Return + return; + } + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + } + + // Initialize dependencies + initializeDependencies() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return performing dependencies initializations + return Promise.all(Application.DEPENDENCIES_INITIALIZATIONS.map(function(dependencyInitialization) { + + // Return performing dependency initialization + return dependencyInitialization(); + + })).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Check if is an extesnion + if(Common.isExtension() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to initialize dependencies.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this extension to try again.')); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to initialize dependencies.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this app to try again.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to initialize dependencies.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Refresh this site to try again.')); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + }); + }); + } + + // Initialize extension + initializeExtension() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return initializing extension + return Extension.initialize().then(function() { + + // Check if closing when done processing extension requests + if(Extension.getCloseWhenDone() === true) { + + // Set ignore updates + self.ignoreUpdates = true; + } + + // Otherwise + else { + + // Set that body display shows that it's using listener + self.bodyDisplay.addClass("usingListener"); + } + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), Message.createText(error), false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Prevent extension from interrupting on close + Extension.preventInterruptOnClose(); + + // Close + window.close(); + } + }); + }); + }); + } + + // Uninitialize dependencies + uninitializeDependencies() { + + // Go through all dependencies uninitializations + for(var i = 0; i < Application.DEPENDENCIES_UNINITIALIZATIONS["length"]; ++i) { + + // Get dependency's uninitialization + var dependencyUninitialization = Application.DEPENDENCIES_UNINITIALIZATIONS[i]; + + // Perform dependency's uninitialization + dependencyUninitialization(); + } + } + + // Show reset settings + showResetSettings() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Get URL parameters + var urlParameters = Common.getUrlParameters(); + + // Check if resetting settings and URL parameters don't contain a request + if(self.resetSettings === true && "Request" in urlParameters === false) { + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Reset Settings'), Message.createText(Language.getDefaultTranslation('Are you sure you want to reset the settings to their default values?')), false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + }, Language.getDefaultTranslation('No'), Language.getDefaultTranslation('Yes'), true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Check if resetting settings + if(messageResult === Message.SECOND_BUTTON_CLICKED_RESULT) { + + // Show loading + self.showLoading(); + + // Set that message second button is loading + self.message.setButtonLoading(Message.SECOND_BUTTON); + + // Prevent showing messages + self.message.prevent(); + + // Set timeout + setTimeout(function() { + + // Once database is initialized + Database.onceInitialized(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return deleting settings + return self.settings.deleteValues(false).then(function() { + + // Return hiding message + return self.message.hide().then(function() { + + setTimeout(function() { + + // Resolve + resolve(); + + }, Application.RESET_SETTINGS_AFTER_DELAY_MILLISECONDS); + }); + + // Catch errors + }).catch(function(error) { + + // Return showing message and allow showing messages + return self.message.show(Language.getDefaultTranslation('Reset Settings Error'), Message.createText(error), true, function() { + + // Hide loading + self.hideLoading(); + + }, Language.getDefaultTranslation('OK'), Message.NO_BUTTON, true).then(function(messageResult) { + + // Check if message was displayed + if(messageResult !== Message.NOT_DISPLAYED_RESULT) { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Return hiding message + return self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + }); + }); + }); + }, true); + + // Resolve + resolve(); + + }, Application.RESET_SETTINGS_BEFORE_DELAY_MILLISECONDS); + } + + // Otherwise + else { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + } + } + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + } + + // Is primary instance + isPrimaryInstance() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Try + try { + + // Create instance + var instance = new Instance(); + } + + // Catch errors + catch(error) { + + // Check if is an extesnion + if(Common.isExtension() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to determine the primary instance.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this extension to try again.')); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to determine the primary instance.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Restart this app to try again.')); + } + + // Otherwise + else { + + // Set message + var message = Message.createText(Language.getDefaultTranslation('Failed to determine the primary instance.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Refresh this site to try again.')); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), message, false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + // Hide loading display spinner + self.loadingDisplay.children("div.spinner").addClass("hide"); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + + // Return + return; + } + + // Return getting if instance is the primary instance + return instance.isPrimaryInstance().then(function(isPrimaryInstance) { + + // Check if instance is the primary instance + if(isPrimaryInstance === true) + + // Resolve + resolve(); + + // Otherwise + else { + + // Check if is an extension + if(Common.isExtension() === true) { + + // Set message + var message = Language.getDefaultTranslation('Only one instance of this extension can be open at once. Close all other instances to continue.'); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('Only one instance of this app can be open at once. Close all other instances to continue.'); + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('Only one instance of this site can be open at once. Close all other instances to continue.'); + } + + // Show message and allow showing messages + self.message.show(Language.getDefaultTranslation('Error'), Message.createText(message), false, function() { + + // Hide loading + self.hideLoading(); + + // Show language display + Language.showDisplay(); + + }, Message.NO_BUTTON, Message.NO_BUTTON, true); + + // Instance is primary instance event + $(instance).one(Instance.IS_PRIMARY_INSTANCE_EVENT, function() { + + // Set timeout + setTimeout(function() { + + // Show loading + self.showLoading(); + + // Prevent showing messages + self.message.prevent(); + + // Hide message + self.message.hide().then(function() { + + // Resolve + resolve(); + }); + + }, Application.HIDE_PRIMARY_INSTANCE_MESSAGE_DELAY_MILLISECONDS); + }); + } + }); + }); + } + + // Set verify source + setVerifySource() { + + // Check if is an extension + if(Common.isExtension() === true) { + + // Check if using the Firefox extension + if(typeof browser !== "undefined" && browser["runtime"]["id"] === "{d783f67c-4dea-4d64-bfc2-1d4a6057babe}") { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Firefox Add-ons site at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + "https://addons.mozilla.org/en-US/firefox/addon/mwc-wallet/", + + // URL + "https://addons.mozilla.org/en-US/firefox/addon/mwc-wallet/", + + // Is external + true, + + // Is blob + false + ] + ]; + } + + // Otherwise check if using the Chrome extension + else if(typeof chrome !== "undefined" && chrome["runtime"]["id"] === "ahhdnimkkpkmclgcnbchlgijhmieongp") { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you installed this extension for free from MWC Wallet\'s official browser extension listing on the Chrome Web Store at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + "https://chromewebstore.google.com/detail/mwc-wallet/ahhdnimkkpkmclgcnbchlgijhmieongp", + + // URL + "https://chromewebstore.google.com/detail/mwc-wallet/ahhdnimkkpkmclgcnbchlgijhmieongp", + + // Is external + true, + + // Is blob + false + ] + ]; + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you downloaded this extension for free from MWC Wallet\'s official browser extension releases at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + "https://github.com/NicolasFlamel1/MWC-Wallet-Browser-Extension/releases", + + // URL + "https://github.com/NicolasFlamel1/MWC-Wallet-Browser-Extension/releases", + + // Is external + true, + + // Is blob + false + ] + ]; + } + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you installed this app for free from MWC Wallet\'s official site at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + "https://mwcwallet.com", + + // URL + "https://mwcwallet.com", + + // Is external + true, + + // Is blob + false + ] + ]; + } + + // Otherwise check if loading from a file + else if(location["protocol"] === Common.FILE_PROTOCOL) { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you downloaded this file for free from MWC Wallet\'s official standalone releases at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + "https://github.com/NicolasFlamel1/MWC-Wallet-Standalone/releases", + + // URL + "https://github.com/NicolasFlamel1/MWC-Wallet-Standalone/releases", + + // Is external + true, + + // Is blob + false + ] + ]; + } + + // Otherwise + else { + + // Set message + var message = Language.getDefaultTranslation('Make sure that you\'re accessing MWC Wallet for free from its official site at %1$m'); + + // Set message arguments + var messageArguments = [ + + [ + // Text + (Tor.isOnionService() === true) ? "http://mwcwalletmiq3gdkmfbqlytxunvlxyli4m6zrqozk7xjc353ewqb6bad.onion" : "https://mwcwallet.com", + + // URL + (Tor.isOnionService() === true) ? "http://mwcwalletmiq3gdkmfbqlytxunvlxyli4m6zrqozk7xjc353ewqb6bad.onion" : "https://mwcwallet.com", + + // Is external + true, + + // Is blob + false + ] + ]; + } + + // Create verify source from message + var verifySource = Language.createTranslatableContainer("

    ", message, messageArguments, "verifySource"); + + // Set create display's verify source + this.createDisplay.find("p.verifySource").replaceWith(verifySource); + + // Set unlock display's verify source + this.unlockDisplay.find("p.verifySource").replaceWith(verifySource); + } + + // Show create or unlock + showCreateOrUnlock() { + + // Check if wallets don't exist + if(this.wallets.exist() === false) + + // Set display to show to create display + var displayToShow = this.createDisplay; + + // Otherwise + else + + // Set display to show to unlock display + var displayToShow = this.unlockDisplay; + + // Hide loading display spinner + this.loadingDisplay.children("div.spinner").addClass("hide"); + + // Allow showing logo + this.logo.allowShowing(); + + // Show logo + this.logo.show(); + + // Set self + var self = this; + + // Loading display spinner transition end or timeout event + this.loadingDisplay.children("div.spinner").transitionEndOrTimeout(function() { + + // Hide loading display + self.loadingDisplay.addClass("hide"); + + // Hide loading + self.hideLoading(); + + // Disable tabbing to everything in display to show and disable everything in display to show + displayToShow.find("*").disableTab().disable(); + + // Show display to show + displayToShow.removeClass("hide"); + + // Set timeout + setTimeout(function() { + + // Trigger input on display to show's inputs + displayToShow.find("input:not(.hide)").trigger("input"); + + // Show display to show children + displayToShow.children().removeClass("hide"); + + // Show language display + Language.showDisplay(true); + + // Set info display + self.infoDisplay.append(Language.createTranslatableContainer("

    ", Language.getDefaultTranslation('%1$x/%2$x/v%3$v'), [Consensus.walletTypeToText(Consensus.getWalletType()), Consensus.networkTypeToText(Consensus.getNetworkType()), VERSION_NUMBER])); + + // Show info display + self.infoDisplay.removeClass("hide"); + + // Initialize focus on input + var focusOnInput = true; + + // Check if cookie acceptance was shown + if(self.cookieAcceptance.show() === true) { + + // Clear focus on input + focusOnInput = false; + } + + // Show maintenance notification + self.maintenanceNotification.show(); + + // Display to show form transition end or timeout event + displayToShow.children("form").transitionEndOrTimeout(function() { + + // Set everything to transition at normal speed + self.mainDisplay.addClass("normalTransitionSpeed"); + + // Enable tabbing to everything in display to show and enable everything in display to show + displayToShow.find("*").enableTab().enable(); + + // Check if focusing on input + if(focusOnInput === true) { + + // Get focus to display's first input + var firstInput = displayToShow.find("input:visible").first(); + + // Try + try { + + // Check if first input isn't autofilled in + if(firstInput.is(":-webkit-autofill") === false && firstInput.is(":autofill") === false) { + + // Focus on first input + firstInput.focus(); + } + } + + // Catch errors + catch(error) { + + // Focus on first input + firstInput.focus(); + } + } + + // Allow showing messages + self.message.allow(); + + }, "opacity"); + }, 0); + + }, "opacity"); + } + + // Show display + showDisplay(displayToShow) { + + // Delete all saved focus + this.focus.deleteAll(); + + // Allow scroll keys + this.scroll.allowKeys(); + + // Disable unlocked + this.unlocked.disable(); + + // Prevent automatic lock + this.automaticLock.prevent(); + + // Lock wallets + this.wallets.lock(this.unlockedAtLeastOnce === false); + + // Check if message is shown + if(this.message.isShown() === true) { + + // Check display to show + switch(displayToShow) { + + // Create display + case this.createDisplay: + + // Set hide message to if message visible state doesn't include create + var hideMessage = (this.message.visibleState() & Message.VISIBLE_STATE_CREATE) === 0; + + // Break + break; + + // Unlock display + case this.unlockDisplay: + + // Set hide message to if message visible state doesn't include unlock + var hideMessage = (this.message.visibleState() & Message.VISIBLE_STATE_UNLOCK) === 0; + + // Break + break; + } + + // Check if hiding message + if(hideMessage === true) { + + // Hide message + this.message.hide(); + } + } + + // Set self + var self = this; + + // Message show application show display event + $(this.message).on(Message.SHOW_EVENT + ".applicationShowDisplay", function() { + + // Check display to show + switch(displayToShow) { + + // Create display + case self.createDisplay: + + // Set hide message to if message visible state doesn't include create + var hideMessage = (self.message.visibleState() & Message.VISIBLE_STATE_CREATE) === 0; + + // Break + break; + + // Unlock display + case self.unlockDisplay: + + // Set hide message to if message visible state doesn't include unlock + var hideMessage = (self.message.visibleState() & Message.VISIBLE_STATE_UNLOCK) === 0; + + // Break + break; + } + + // Check if hiding message + if(hideMessage === true) { + + // Hide message + self.message.hide(); + } + + // Message before show application event + }).on(Message.BEFORE_SHOW_EVENT + ".application", function() { + + // Check display to show + switch(displayToShow) { + + // Create display + case self.createDisplay: + + // Set hide message to if message visible state doesn't include create + var hideMessage = (self.message.visibleState() & Message.VISIBLE_STATE_CREATE) === 0; + + // Break + break; + + // Unlock display + case self.unlockDisplay: + + // Set hide message to if message visible state doesn't include unlock + var hideMessage = (self.message.visibleState() & Message.VISIBLE_STATE_UNLOCK) === 0; + + // Break + break; + } + + // Check if hiding message + if(hideMessage === true) { + + // Return false to cancel the message + return false; + } + }); + + // Hide unlocked display children + this.unlockedDisplay.children().addClass("hide"); + + // Hide display to show display logo + displayToShow.children("div.logo").addClass("hide"); + + // Unlocked display children transition end or timeout event + this.unlockedDisplay.children().transitionEndOrTimeout(function() { + + // Hide unlocked display + self.unlockedDisplay.addClass("hide"); + + // Hide loading + self.hideLoading(); + + // Enable unlocked + self.unlocked.enable(); + + // Reset unlocked + self.unlocked.reset(); + + // Disable tabbing to everything in display to show and disable everything in display to show + displayToShow.find("*").disableTab().disable(); + + // Show display to show + displayToShow.removeClass("hide"); + + // Set timeout + setTimeout(function() { + + // Turn off message show application show display event + $(self.message).off(Message.SHOW_EVENT + ".applicationShowDisplay"); + + // Turn off message before show application event + $(self.message).off(Message.BEFORE_SHOW_EVENT + ".application"); + + // Show display to show children + displayToShow.children().removeClass("hide"); + + // Show logo + self.logo.show(); + + // Show info display + self.infoDisplay.removeClass("hide"); + + // Check if message is not shown + if(self.message.isShown() === false) { + + // Enable tabbing to everything in display to show and enable everything in display to show + displayToShow.find("*").enableTab().enable(); + + // Focus on display to show + displayToShow.focus(); + } + + // Otherwise + else { + + // Display to show form transition end or timeout event + displayToShow.children("form").transitionEndOrTimeout(function() { + + // Check if message is not shown + if(self.message.isShown() === false) { + + // Enable tabbing to everything in display to show and enable everything in display to show + displayToShow.find("*").enableTab().enable(); + + // Focus on display to show + displayToShow.focus(); + } + }, "opacity"); + } + + // Allow showing messages + self.message.allow(); + }, 0); + + }, "opacity"); + } + + // Reset + reset() { + + // Set title + var title = Language.getDefaultTranslation('Show'); + + // Show create display password show icon, set its title, and change password input type + this.createDisplay.find("input[name=\"Password\"]").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + + // Show create display confirm password show icon, set its title, and change password input type + this.createDisplay.find("input[name=\"Confirm Password\"]").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + + // Show unlock display password show icon, set its title, and change password input type + this.unlockDisplay.find("input[name=\"Password\"]").siblings("span.show").removeClass("conceal").attr(Common.DATA_ATTRIBUTE_PREFIX + "text", title).attr("title", Language.getTranslation(title)).siblings("input").attr("type", "password"); + } + + // Private browsing message shown local storage name + static get PRIVATE_BROWSING_MESSAGE_SHOWN_LOCAL_STORAGE_NAME() { + + // Return private browsing message shown local storage name + return "Private Browsing Message Shown"; + } + + // Private browsing message shown true value + static get PRIVATE_BROWSING_MESSAGE_SHOWN_TRUE_VALUE() { + + // Return private browsing message shown true value + return "true"; + } + + // Third-party cookies message shown local storage name + static get THIRD_PARTY_COOKIES_MESSAGE_SHOWN_LOCAL_STORAGE_NAME() { + + // Return third-party cookies message shown local storage name + return "Third-Party Cookies Message Shown"; + } + + // Third-party cookies message shown true value + static get THIRD_PARTY_COOKIES_MESSAGE_SHOWN_TRUE_VALUE() { + + // Return Third-party cookies message shown true value + return "true"; + } + + // Show install app minimum delay seconds + static get SHOW_INSTALL_APP_MINIMUM_DELAY_SECONDS() { + + // Return show install app minimum delay seconds + return 1 * Common.SECONDS_IN_A_MINUTE; + } + + // Show install app maximum delay seconds + static get SHOW_INSTALL_APP_MAXIMUM_DELAY_SECONDS() { + + // Return show install app maximum delay seconds + return 5 * Common.SECONDS_IN_A_MINUTE; + } + + // Show unlocked display delay milliseconds + static get SHOW_UNLOCKED_DISPLAY_DELAY_MILLISECONDS() { + + // Return show unlocked display delay milliseconds + return 300; + } + + // Delete all wallets delay milliseconds + static get DELETE_ALL_WALLETS_DELAY_MILLISECONDS() { + + // Return delete all wallets delay milliseconds + return 300; + } + + // Hide primary instance message delay milliseconds + static get HIDE_PRIMARY_INSTANCE_MESSAGE_DELAY_MILLISECONDS() { + + // Return hide primary instance message delay milliseconds + return 100; + } + + // Hide private browsing message delay milliseconds + static get HIDE_PRIVATE_BROWSING_MESSAGE_DELAY_MILLISECONDS() { + + // Return hide private browsing message delay milliseconds + return Application.HIDE_PRIMARY_INSTANCE_MESSAGE_DELAY_MILLISECONDS; + } + + // Hide third-party cookies message delay milliseconds + static get HIDE_THIRD_PARTY_COOKIES_MESSAGE_DELAY_MILLISECONDS() { + + // Return hide third-party cookies message delay milliseconds + return Application.HIDE_PRIMARY_INSTANCE_MESSAGE_DELAY_MILLISECONDS; + } + + // Reset settings before delay milliseconds + static get RESET_SETTINGS_BEFORE_DELAY_MILLISECONDS() { + + // Return reset settings before delay milliseconds + return 500; + } + + // Reset settings after delay milliseconds + static get RESET_SETTINGS_AFTER_DELAY_MILLISECONDS() { + + // Return reset settings after delay milliseconds + return 500; + } + + // Install update delay milliseconds + static get INSTALL_UPDATE_DELAY_MILLISECONDS() { + + // Return install update delay milliseconds + return 1 * Common.MILLISECONDS_IN_A_SECOND; + } + + // Delete all wallets button maximum width + static get DELETE_ALL_WALLETS_BUTTON_MAXIMUM_WIDTH() { + + // Return delete all wallets button maximum width + return parseFloat("10em"); + } + + // Dependencies initializations + static get DEPENDENCIES_INITIALIZATIONS() { + + // Return dependencies initializations + return [ + + // BLAKE2b initialize + Blake2b.initialize, + + // Ed25519 initialize + Ed25519.initialize, + + // X25519 initialize + X25519.initialize, + + // Secp256k1-zkp initialize + Secp256k1Zkp.initialize, + + // SMAZ initialize + Smaz.initialize, + + // Database initialize + Database.initialize, + + // Output initialize + Output.initialize, + + // Slate initialize + Slate.initialize, + + // Wallet initialize + Wallet.initialize, + + // Tor initialize + Tor.initialize, + + // Camera initialize + Camera.initialize + ]; + } + + // Dependencies uninitializations + static get DEPENDENCIES_UNINITIALIZATIONS() { + + // Return dependencies uninitializations + return [ + + // Secp256k1-zkp uninitialize + Secp256k1Zkp.uninitialize + ]; + } + + // Settings enable node connection error messages name + static get SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_NAME() { + + // Return settings enable node connection error messages name + return "Enable Node Connection Error Messages"; + } + + // Settings enable node connection error messages default value + static get SETTINGS_ENABLE_NODE_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE() { + + // Return settings enable node connection error messages default value + return true; + } + + // Settings enable listener connection error messages name + static get SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_NAME() { + + // Return settings enable listener connection error messages name + return "Enable Listener Connection Error Messages"; + } + + // Settings enable listener connection error messages default value + static get SETTINGS_ENABLE_LISTENER_CONNECTION_ERROR_MESSAGES_DEFAULT_VALUE() { + + // Return settings enable listener connection error messages default value + return true; + } + + // Shift key code + static get SHIFT_KEY_CODE() { + + // Return shift key code + return 16; + } + + // Prevent minimal interface delay milliseconds + static get PREVENT_MINIMAL_INTERFACE_DELAY_MILLISECONDS() { + + // Return prevent minimal interface delay milliseconds + return 500; + } + + // Canceled check interval milliseconds + static get CANCELED_CHECK_INTERVAL_MILLISECONDS() { + + // Return canceled check interval milliseconds + return 50; + } + + // Check extension request received interval milliseconds + static get CHECK_EXTENSION_REQUEST_RECEIVED_INTERVAL_MILLISECONDS() { + + // Return check extension request received interval milliseconds + return 50; + } + + // Check hardware wallet priority interval milliseconds + static get CHECK_HARDWARE_WALLET_PRIORITY_INTERVAL_MILLISECONDS() { + + // Return check hardware wallet priority interval milliseconds + return 50; + } +} + + +// Main function + +// Set global object's application +globalThis["Application"] = Application; + +// Ready event +$(function() { + + // Set timeout + setTimeout(function() { + + // Check if a startup error didn't occur + if(startupErrorOccurred() === false) { + + // Enable application error handler + enableApplicationErrorHandler(); + + // Create application + new Application(); + } + + }, Application.SHOW_LOADING_DELAY_MILLISECONDS); +}); diff --git a/scripts/automatic_lock.js b/scripts/automatic_lock.js new file mode 100755 index 0000000..cf7a8ca --- /dev/null +++ b/scripts/automatic_lock.js @@ -0,0 +1,539 @@ +// Use strict +"use strict"; + + +// Classes + +// Automatic lock class +class AutomaticLock { + + // Public + + // Constructor + constructor(application, message, settings) { + + // Set application + this.application = application; + + // Set message + this.message = message; + + // Set settings + this.settings = settings; + + // Set lock timeout to no timeout + this.lockTimeout = AutomaticLock.NO_TIMEOUT; + + // Set allow no interaction lock to false + this.allowNoInteractionLock = false; + + // Set allow inactive lock to false + this.allowInactiveLock = false; + + // Set locking to false + this.locking = false; + + // Set enable automatic lock to setting's default value + this.enableAutomaticLock = AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_DEFAULT_VALUE; + + // Set no interaction lock timeout minutes to setting's default value + this.noInteractionLockTimeoutMinutes = AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_DEFAULT_VALUE; + + // Set lock on inactive to setting's default value + this.enableLockOnInactive = AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_DEFAULT_VALUE; + + // Update last interaction timestamp + this.updateLastInteractionTimestamp(); + + // Set self + var self = this; + + // Once database is initialized + Database.onceInitialized(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return creating settings + return Promise.all([ + + // Create enable automatic lock setting + self.settings.createValue(AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_NAME, AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_DEFAULT_VALUE), + + // Create no interaction lock timeout minutes setting + self.settings.createValue(AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_NAME, AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_DEFAULT_VALUE), + + // Create enable lock on inactive setting + self.settings.createValue(AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_NAME, AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_DEFAULT_VALUE) + + ]).then(function() { + + // Initialize settings + var settings = [ + + // Enable automatic lock setting + AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_NAME, + + // No interaction lock timeout minutes setting + AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_NAME, + + // Enable lock on inactive setting + AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_NAME + ]; + + // Return getting settings' values + return Promise.all(settings.map(function(setting) { + + // Return getting setting's value + return self.settings.getValue(setting); + + })).then(function(settingValues) { + + // Set enable automatic lock to setting's value + self.enableAutomaticLock = settingValues[settings.indexOf(AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_NAME)]; + + // Set no interaction lock timeout minutes to setting's value + self.noInteractionLockTimeoutMinutes = settingValues[settings.indexOf(AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_NAME)]; + + // Set enable lock on inactive to setting's value + self.enableLockOnInactive = settingValues[settings.indexOf(AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_NAME)]; + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + }); + + // Settings change event + $(this.settings).on(Settings.CHANGE_EVENT, function(event, setting) { + + // Check what setting was changes + switch(setting[Settings.DATABASE_SETTING_NAME]) { + + // Enable automatic lock setting + case AutomaticLock.SETTINGS_ENABLE_AUTOMATIC_LOCK_NAME: + + // Set enable automatic lock to setting's value + self.enableAutomaticLock = setting[Settings.DATABASE_VALUE_NAME]; + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Break + break; + + // No interaction lock timeout minutes setting + case AutomaticLock.SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_NAME: + + // Set no interaction lock timeout minutes to setting's value + self.noInteractionLockTimeoutMinutes = setting[Settings.DATABASE_VALUE_NAME]; + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Break + break; + + // Enable lock on inactive setting + case AutomaticLock.SETTINGS_ENABLE_LOCK_ON_INACTIVE_NAME: + + // Set enable lock on inactive to setting's value + self.enableLockOnInactive = setting[Settings.DATABASE_VALUE_NAME]; + + // Break + break; + } + }); + + // Window blur event + $(window).on("blur", function() { + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Check if lock on inactive is enabled and allowed + if(self.enableLockOnInactive === true && self.allowInactiveLock === true) { + + // Reset lock timeout + self.resetLockTimeout(); + + // Check if application's unlocked display is shown + if(self.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Inactive lock activated.')); + } + + // Lock + self.lock(); + } + + // Window focus event + }).on("focus", function() { + + // Check if no interaction lock timeout minutes exist + if(self.noInteractionLockTimeoutMinutes !== AutomaticLock.NO_NO_INTERACTION_LOCK_TIMEOUT_MINUTES) { + + // Check if application hasn't been interacted with in too long + if(self.lastInteractionTimestamp <= Common.getCurrentTimestamp() - self.noInteractionLockTimeoutMinutes * Common.SECONDS_IN_A_MINUTE) { + + // Reset lock timeout + self.resetLockTimeout(); + + // Check if automatic lock is enabled and allowed + if(self.enableAutomaticLock === true && self.allowNoInteractionLock === true) { + + // Check if application's unlocked display is shown + if(self.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Automatic lock activated.')); + } + + // Lock + self.lock(); + } + } + } + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Window page hide event + }).on("pagehide", function() { + + // Check if lock on inactive is enabled and allowed + if(self.enableLockOnInactive === true && self.allowInactiveLock === true) { + + // Reset lock timeout + self.resetLockTimeout(); + + // Check if application's unlocked display is shown + if(self.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Inactive lock activated.')); + } + + // Lock + self.lock(); + } + }); + + // Document mouse move, mouse down, mouse up, key down, key up, touch start, touch move, touch end, and focus change event + $(document).on("mousemove mousedown mouseup keydown keyup touchstart touchmove touchend" + Common.FOCUS_CHANGE_EVENT, function(event) { + + // Check if has focus + if(document.hasFocus() === true) + + // Update last interaction timestamp + self.updateLastInteractionTimestamp(); + + // Document visibility change event + }).on("visibilitychange", function() { + + // Check if page is hidden + if(document["visibilityState"] === Common.VISIBILITY_STATE_HIDDEN) { + + // Check if lock on inactive is enabled and allowed + if(self.enableLockOnInactive === true && self.allowInactiveLock === true) { + + // Reset lock timeout + self.resetLockTimeout(); + + // Check if application's unlocked display is shown + if(self.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Inactive lock activated.')); + } + + // Lock + self.lock(); + } + } + }); + } + + // Allow + allow(lockType = AutomaticLock.NO_INTERACTION_LOCK_TYPE | AutomaticLock.INACTIVE_LOCK_TYPE) { + + // Check if allowing the no interaction lock + if((lockType & AutomaticLock.NO_INTERACTION_LOCK_TYPE) !== 0) + + // Set allow no interaction lock + this.allowNoInteractionLock = true; + + // Check if allowing the inactive lock + if((lockType & AutomaticLock.INACTIVE_LOCK_TYPE) !== 0) + + // Set allow inactive lock + this.allowInactiveLock = true; + + // Check if no interaction lock timeout minutes exist + if(this.noInteractionLockTimeoutMinutes !== AutomaticLock.NO_NO_INTERACTION_LOCK_TIMEOUT_MINUTES) { + + // Check if application hasn't been interacted with in too long + if(this.lastInteractionTimestamp <= Common.getCurrentTimestamp() - this.noInteractionLockTimeoutMinutes * Common.SECONDS_IN_A_MINUTE) { + + // Reset lock timeout + this.resetLockTimeout(); + + // Check if automatic lock is enabled and allowed + if(this.enableAutomaticLock === true && this.allowNoInteractionLock === true) { + + // Check if application's unlocked display is shown + if(this.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Automatic lock activated.')); + } + + // Lock + this.lock(); + } + } + } + + // Check if lock on inactive is enabled and allowed and the document doesn't have focus or is hidden + if(this.enableLockOnInactive === true && this.allowInactiveLock === true && (document.hasFocus() === false || document["visibilityState"] === Common.VISIBILITY_STATE_HIDDEN)) { + + // Reset lock timeout + this.resetLockTimeout(); + + // Check if application's unlocked display is shown + if(this.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Inactive lock activated.')); + } + + // Lock + this.lock(); + } + } + + // Get allowed + getAllowed() { + + // Initialize result + var result = 0; + + // Check if allow no interaction lock + if(this.allowNoInteractionLock === true) { + + // Set no interaction lock type in the result + result |= AutomaticLock.NO_INTERACTION_LOCK_TYPE; + } + + // Check if allow inactive lock + if(this.allowInactiveLock === true) { + + // Set inactive lock type in the result + result |= AutomaticLock.INACTIVE_LOCK_TYPE; + } + + // Return result + return result; + } + + // Prevent + prevent(lockType = AutomaticLock.NO_INTERACTION_LOCK_TYPE | AutomaticLock.INACTIVE_LOCK_TYPE) { + + // Check if preventing the no interaction lock + if((lockType & AutomaticLock.NO_INTERACTION_LOCK_TYPE) !== 0) + + // Clear allow no interaction lock + this.allowNoInteractionLock = false; + + // Check if preventing the inactive lock + if((lockType & AutomaticLock.INACTIVE_LOCK_TYPE) !== 0) + + // Clear allow inactive lock + this.allowInactiveLock = false; + } + + // Is locking + isLocking() { + + // Return if locking + return this.locking === true; + } + + // No interaction lock type + static get NO_INTERACTION_LOCK_TYPE() { + + // Return no interaction lock type + return 1 << 0; + } + + // Inactive lock type + static get INACTIVE_LOCK_TYPE() { + + // Return inactive lock type + return 1 << 1; + } + + // Private + + // Update last interaction timestamp + updateLastInteractionTimestamp() { + + // Set last interaction timestamp to current timestamp + this.lastInteractionTimestamp = Common.getCurrentTimestamp(); + + // Reset lock timeout + this.resetLockTimeout(); + } + + // Reset lock timeout + resetLockTimeout() { + + // Check if lock timeout exists + if(this.lockTimeout !== AutomaticLock.NO_TIMEOUT) { + + // Clear lock timeout + clearTimeout(this.lockTimeout); + + // Set lock timeout to no timeout + this.lockTimeout = AutomaticLock.NO_TIMEOUT; + } + + // Check if no interaction lock timeout minutes exist + if(this.noInteractionLockTimeoutMinutes !== AutomaticLock.NO_NO_INTERACTION_LOCK_TIMEOUT_MINUTES) { + + // Set self + var self = this; + + // Set lock timeout + this.lockTimeout = setTimeout(function() { + + // Reset lock timeout + self.resetLockTimeout(); + + // Check if automatic lock is enabled and allowed + if(self.enableAutomaticLock === true && self.allowNoInteractionLock === true) { + + // Check if application's unlocked display is shown + if(self.application.isUnlockedDisplayShown() === true) { + + // Log message + Log.logMessage(Language.getDefaultTranslation('Automatic lock activated.')); + } + + // Lock + self.lock(); + } + + }, this.noInteractionLockTimeoutMinutes * Common.SECONDS_IN_A_MINUTE * Common.MILLISECONDS_IN_A_SECOND); + } + } + + // Lock + lock() { + + // Check if application's unlocked display is shown, not already locking, and not currently showing a minimal display + if(this.application.isUnlockedDisplayShown() === true && this.isLocking() === false && $("div.unlocked").hasClass("minimal") === false) { + + // Set locking + this.locking = true; + + // Prevent showing messages + this.message.prevent(); + + // Show loading + this.application.showLoading(); + + // Show lock display + this.application.showLockDisplay(); + + // Set self + var self = this; + + // Set timeout + setTimeout(function() { + + // Clear locking + self.locking = false; + }, 0); + } + } + + // No timeout + static get NO_TIMEOUT() { + + // Return no timeout + return null; + } + + // No no interaction lock timeout minutes + static get NO_NO_INTERACTION_LOCK_TIMEOUT_MINUTES() { + + // Return no no interaction lock timeout minutes + return null; + } + + // Settings enable automatic lock name + static get SETTINGS_ENABLE_AUTOMATIC_LOCK_NAME() { + + // Return settings enable automatic lock name + return "Enable Automatic Lock"; + } + + // Settings enable automatic lock default value + static get SETTINGS_ENABLE_AUTOMATIC_LOCK_DEFAULT_VALUE() { + + // Return settings enable automatic lock default value + return true; + } + + // Settings no interaction lock timeout minutes name + static get SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_NAME() { + + // Return settings no interaction lock timeout minutes name + return "No Interaction Lock Timeout Minutes"; + } + + // Settings no interaction lock timeout minutes default value + static get SETTINGS_NO_INTERACTION_LOCK_TIMEOUT_MINUTES_DEFAULT_VALUE() { + + // Return settings no interaction lock timeout minutes default value + return 5; + } + + // Settings enable lock on inactive name + static get SETTINGS_ENABLE_LOCK_ON_INACTIVE_NAME() { + + // Return settings enable lock on inactive name + return "Enable Lock On Inactive"; + } + + // Settings enable lock on inactive default value + static get SETTINGS_ENABLE_LOCK_ON_INACTIVE_DEFAULT_VALUE() { + + // Return settings enable lock on inactive default value + return false; + } +} + + +// Main function + +// Set global object's automatic lock +globalThis["AutomaticLock"] = AutomaticLock; diff --git a/scripts/base58.js b/scripts/base58.js new file mode 100755 index 0000000..756789c --- /dev/null +++ b/scripts/base58.js @@ -0,0 +1,266 @@ +// Use strict +"use strict"; + + +// Classes + +// Base58 class +class Base58 { + + // Public + + // Encode + static encode(byteArray) { + + // Go through all leading zeros in the byte array + var numberOfLeadingZeros = 0; + while(numberOfLeadingZeros < byteArray["length"] && byteArray[numberOfLeadingZeros] === 0) { + + // Increment number of leading zeros + ++numberOfLeadingZeros; + } + + // Get buffer size + var bufferSize = Math.ceil((byteArray["length"] - numberOfLeadingZeros) * Base58.BYTES_PER_LENGTH_IN_NEW_BASE); + + // Create buffer + var buffer = (new Uint8Array(bufferSize)).fill(0); + + // Go through all bytes in the byte array after the leading zeros + var length = 0; + for(var i = numberOfLeadingZeros; i < byteArray["length"]; ++i) { + + // Get byte + var byte = byteArray[i]; + + // Go through all base 58 components of the byte + for(var j = 0, k = bufferSize - 1; (byte !== 0 || j < length) && k >= 0; ++j, --k) { + + // Include the current buffer value in the byte + byte += (Common.BYTE_MAX_VALUE + 1) * buffer[k]; + + // Set value in the buffer + buffer[k] = byte % Base58.NUMBER_BASE; + + // Update the byte + byte = Math.floor(byte / Base58.NUMBER_BASE); + } + + // Update length + length = j; + } + + // Go through all leading zeros in the buffer + var bufferIndex = bufferSize - length; + while(bufferIndex < buffer["length"] && buffer[bufferIndex] === 0) { + + // Increment buffer index + ++bufferIndex; + } + + // Set result to start with the number of leading zeros in base58 + var result = Base58.CHARACTERS[0].repeat(numberOfLeadingZeros); + + // Go through all bytes in the buffer after the leading zeros + for(; bufferIndex < buffer["length"]; ++bufferIndex) { + + // Append buffer's value in base58 to the result + result += Base58.CHARACTERS[buffer[bufferIndex]]; + } + + // Return result + return result; + } + + // Encode with checksum + static encodeWithChecksum(byteArray) { + + // Get the checksum of the byte array + var checksum = Base58.getChecksum(byteArray); + + // Return encoding the byte array with the checksum + return Base58.encode(Common.mergeArrays([ + + // Byte array + byteArray, + + // Checksum + checksum + ])); + } + + // Decode + static decode(base58String) { + + // Go through all leading zeros in base58 in the string + var numberOfLeadingZeros = 0; + while(numberOfLeadingZeros < base58String["length"] && base58String[numberOfLeadingZeros] === Base58.CHARACTERS[0]) { + + // Increment number of leading zeros + ++numberOfLeadingZeros; + } + + // Get buffer size + var bufferSize = Math.ceil((base58String["length"] - numberOfLeadingZeros) * Base58.BYTES_PER_LENGTH_IN_OLD_BASE); + + // Crete buffer + var buffer = (new Uint8Array(bufferSize)).fill(0); + + // Go through all characters in the string after the leading zeros in base58 + var length = 0; + for(var i = numberOfLeadingZeros; i < base58String["length"]; ++i) { + + // Get character as byte + var byte = Base58.CHARACTERS.indexOf(base58String[i]); + + // Check if byte is invalid + if(byte === Common.INDEX_NOT_FOUND) { + + // Throw error + throw "Invalid base58 string."; + } + + // Go through all base 58 components of the byte + for(var j = 0, k = bufferSize - 1; (byte !== 0 || j < length) && k >= 0; ++j, --k) { + + // Include the current buffer value in the byte + byte += Base58.NUMBER_BASE * buffer[k]; + + // Set value in the buffer + buffer[k] = byte % (Common.BYTE_MAX_VALUE + 1); + + // Update the byte + byte = Math.floor(byte / (Common.BYTE_MAX_VALUE + 1)); + } + + // Update length + length = j; + } + + // Go through all leading zeros in the buffer + var bufferIndex = bufferSize - length; + while(bufferIndex < buffer["length"] && buffer[bufferIndex] === 0) { + + // Increment buffer index + ++bufferIndex; + } + + // Set result to start with the number of leading zeros + var result = (new Uint8Array(numberOfLeadingZeros + bufferSize - bufferIndex)).fill(0); + + // Go through all bytes in the buffer after the leading zeros + for(var i = 0; bufferIndex < buffer["length"]; ++i, ++bufferIndex) { + + // Append buffer's value to the result + result[i + numberOfLeadingZeros] += buffer[bufferIndex]; + } + + // Return result + return result; + } + + // Decode with checksum + static decodeWithChecksum(base58String) { + + // Try + try { + + // Decode the string + var byteArray = Base58.decode(base58String); + } + + // Catch errors + catch(error) { + + // Throw error + throw error; + } + + // Check if the byte array doesn't include a checksum + if(byteArray["length"] < Base58.CHECKSUM_LENGTH) { + + // Throw error + throw "No checksum exists."; + } + + // Otherwise + else { + + // Get the checksum of the byte array without its checksum + var checksum = Base58.getChecksum(byteArray.subarray(0, byteArray["length"] - Base58.CHECKSUM_LENGTH)); + + // Get the provided checksum from the byte array + var providedChecksum = byteArray.subarray(byteArray["length"] - Base58.CHECKSUM_LENGTH); + + // Check if checksums don't match + if(Common.arraysAreEqual(checksum, providedChecksum) === false) { + + // Throw error + throw "Invalid checksum."; + } + + // Otherwise + else { + + // Return byte array without the checksum + return byteArray.subarray(0, byteArray["length"] - Base58.CHECKSUM_LENGTH); + } + } + } + + // Private + + // Get checksum + static getChecksum(byteArray) { + + // Get a hash of the hash of the byte array + var hash = new Uint8Array(sha256.arrayBuffer(new Uint8Array(sha256.arrayBuffer(byteArray)))); + + // Get the checksum from the hash + var checksum = hash.subarray(0, Base58.CHECKSUM_LENGTH); + + // Return checksum + return checksum; + } + + // Characters + static get CHARACTERS() { + + // Return characters + return "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + } + + // Number base + static get NUMBER_BASE() { + + // Return number base + return 58; + } + + // Bytes per length in new base + static get BYTES_PER_LENGTH_IN_NEW_BASE() { + + // Return bytes per length in new base + return Math.log(Common.BYTE_MAX_VALUE + 1) / Math.log(Base58.NUMBER_BASE); + } + + // Bytes per length in old base + static get BYTES_PER_LENGTH_IN_OLD_BASE() { + + // Return bytes per length in old base + return Math.log(Base58.NUMBER_BASE) / Math.log(Common.BYTE_MAX_VALUE + 1); + } + + // Checksum length + static get CHECKSUM_LENGTH() { + + // Return checksum length + return 4; + } +} + + +// Main function + +// Set global object's base58 +globalThis["Base58"] = Base58; diff --git a/scripts/base64.js license.txt b/scripts/base64.js license.txt new file mode 100755 index 0000000..fd579a4 --- /dev/null +++ b/scripts/base64.js license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2014, Dan Kogai +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of {{{project}}} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/scripts/base64.js-3.7.5.js b/scripts/base64.js-3.7.5.js new file mode 100755 index 0000000..c44a6c8 --- /dev/null +++ b/scripts/base64.js-3.7.5.js @@ -0,0 +1,316 @@ +// +// THIS FILE IS AUTOMATICALLY GENERATED! DO NOT EDIT BY HAND! +// +; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + ? module.exports = factory() + : typeof define === 'function' && define.amd + ? define(factory) : + // cf. https://github.com/dankogai/js-base64/issues/119 + (function () { + // existing version for noConflict() + var _Base64 = global.Base64; + var gBase64 = factory(); + gBase64.noConflict = function () { + global.Base64 = _Base64; + return gBase64; + }; + if (global.Meteor) { // Meteor.js + Base64 = gBase64; + } + global.Base64 = gBase64; + })(); +}((typeof self !== 'undefined' ? self + : typeof window !== 'undefined' ? window + : typeof global !== 'undefined' ? global + : this), function () { + 'use strict'; + /** + * base64.ts + * + * Licensed under the BSD 3-Clause License. + * http://opensource.org/licenses/BSD-3-Clause + * + * References: + * http://en.wikipedia.org/wiki/Base64 + * + * @author Dan Kogai (https://github.com/dankogai) + */ + var version = '3.7.5'; + /** + * @deprecated use lowercase `version`. + */ + var VERSION = version; + var _hasatob = typeof atob === 'function'; + var _hasbtoa = typeof btoa === 'function'; + var _hasBuffer = typeof Buffer === 'function'; + var _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined; + var _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined; + var b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + var b64chs = Array.prototype.slice.call(b64ch); + var b64tab = (function (a) { + var tab = {}; + a.forEach(function (c, i) { return tab[c] = i; }); + return tab; + })(b64chs); + var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; + var _fromCC = String.fromCharCode.bind(String); + var _U8Afrom = typeof Uint8Array.from === 'function' + ? Uint8Array.from.bind(Uint8Array) + : function (it) { return new Uint8Array(Array.prototype.slice.call(it, 0)); }; + var _mkUriSafe = function (src) { return src + .replace(/=/g, '').replace(/[+\/]/g, function (m0) { return m0 == '+' ? '-' : '_'; }); }; + var _tidyB64 = function (s) { return s.replace(/[^A-Za-z0-9\+\/]/g, ''); }; + /** + * polyfill version of `btoa` + */ + var btoaPolyfill = function (bin) { + // console.log('polyfilled'); + var u32, c0, c1, c2, asc = ''; + var pad = bin.length % 3; + for (var i = 0; i < bin.length;) { + if ((c0 = bin.charCodeAt(i++)) > 255 || + (c1 = bin.charCodeAt(i++)) > 255 || + (c2 = bin.charCodeAt(i++)) > 255) + throw new TypeError('invalid character found'); + u32 = (c0 << 16) | (c1 << 8) | c2; + asc += b64chs[u32 >> 18 & 63] + + b64chs[u32 >> 12 & 63] + + b64chs[u32 >> 6 & 63] + + b64chs[u32 & 63]; + } + return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc; + }; + /** + * does what `window.btoa` of web browsers do. + * @param {String} bin binary string + * @returns {string} Base64-encoded string + */ + var _btoa = _hasbtoa ? function (bin) { return btoa(bin); } + : _hasBuffer ? function (bin) { return Buffer.from(bin, 'binary').toString('base64'); } + : btoaPolyfill; + var _fromUint8Array = _hasBuffer + ? function (u8a) { return Buffer.from(u8a).toString('base64'); } + : function (u8a) { + // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326 + var maxargs = 0x1000; + var strs = []; + for (var i = 0, l = u8a.length; i < l; i += maxargs) { + strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs))); + } + return _btoa(strs.join('')); + }; + /** + * converts a Uint8Array to a Base64 string. + * @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5 + * @returns {string} Base64 string + */ + var fromUint8Array = function (u8a, urlsafe) { + if (urlsafe === void 0) { urlsafe = false; } + return urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a); + }; + // This trick is found broken https://github.com/dankogai/js-base64/issues/130 + // const utob = (src: string) => unescape(encodeURIComponent(src)); + // reverting good old fationed regexp + var cb_utob = function (c) { + if (c.length < 2) { + var cc = c.charCodeAt(0); + return cc < 0x80 ? c + : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6)) + + _fromCC(0x80 | (cc & 0x3f))) + : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f)) + + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) + + _fromCC(0x80 | (cc & 0x3f))); + } + else { + var cc = 0x10000 + + (c.charCodeAt(0) - 0xD800) * 0x400 + + (c.charCodeAt(1) - 0xDC00); + return (_fromCC(0xf0 | ((cc >>> 18) & 0x07)) + + _fromCC(0x80 | ((cc >>> 12) & 0x3f)) + + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) + + _fromCC(0x80 | (cc & 0x3f))); + } + }; + var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; + /** + * @deprecated should have been internal use only. + * @param {string} src UTF-8 string + * @returns {string} UTF-16 string + */ + var utob = function (u) { return u.replace(re_utob, cb_utob); }; + // + var _encode = _hasBuffer + ? function (s) { return Buffer.from(s, 'utf8').toString('base64'); } + : _TE + ? function (s) { return _fromUint8Array(_TE.encode(s)); } + : function (s) { return _btoa(utob(s)); }; + /** + * converts a UTF-8-encoded string to a Base64 string. + * @param {boolean} [urlsafe] if `true` make the result URL-safe + * @returns {string} Base64 string + */ + var encode = function (src, urlsafe) { + if (urlsafe === void 0) { urlsafe = false; } + return urlsafe + ? _mkUriSafe(_encode(src)) + : _encode(src); + }; + /** + * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5. + * @returns {string} Base64 string + */ + var encodeURI = function (src) { return encode(src, true); }; + // This trick is found broken https://github.com/dankogai/js-base64/issues/130 + // const btou = (src: string) => decodeURIComponent(escape(src)); + // reverting good old fationed regexp + var re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g; + var cb_btou = function (cccc) { + switch (cccc.length) { + case 4: + var cp = ((0x07 & cccc.charCodeAt(0)) << 18) + | ((0x3f & cccc.charCodeAt(1)) << 12) + | ((0x3f & cccc.charCodeAt(2)) << 6) + | (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000; + return (_fromCC((offset >>> 10) + 0xD800) + + _fromCC((offset & 0x3FF) + 0xDC00)); + case 3: + return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12) + | ((0x3f & cccc.charCodeAt(1)) << 6) + | (0x3f & cccc.charCodeAt(2))); + default: + return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6) + | (0x3f & cccc.charCodeAt(1))); + } + }; + /** + * @deprecated should have been internal use only. + * @param {string} src UTF-16 string + * @returns {string} UTF-8 string + */ + var btou = function (b) { return b.replace(re_btou, cb_btou); }; + /** + * polyfill version of `atob` + */ + var atobPolyfill = function (asc) { + // console.log('polyfilled'); + asc = asc.replace(/\s+/g, ''); + if (!b64re.test(asc)) + throw new TypeError('malformed base64.'); + asc += '=='.slice(2 - (asc.length & 3)); + var u24, bin = '', r1, r2; + for (var i = 0; i < asc.length;) { + u24 = b64tab[asc.charAt(i++)] << 18 + | b64tab[asc.charAt(i++)] << 12 + | (r1 = b64tab[asc.charAt(i++)]) << 6 + | (r2 = b64tab[asc.charAt(i++)]); + bin += r1 === 64 ? _fromCC(u24 >> 16 & 255) + : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255) + : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255); + } + return bin; + }; + /** + * does what `window.atob` of web browsers do. + * @param {String} asc Base64-encoded string + * @returns {string} binary string + */ + var _atob = _hasatob ? function (asc) { return atob(_tidyB64(asc)); } + : _hasBuffer ? function (asc) { return Buffer.from(asc, 'base64').toString('binary'); } + : atobPolyfill; + // + var _toUint8Array = _hasBuffer + ? function (a) { return _U8Afrom(Buffer.from(a, 'base64')); } + : function (a) { return _U8Afrom(_atob(a).split('').map(function (c) { return c.charCodeAt(0); })); }; + /** + * converts a Base64 string to a Uint8Array. + */ + var toUint8Array = function (a) { return _toUint8Array(_unURI(a)); }; + // + var _decode = _hasBuffer + ? function (a) { return Buffer.from(a, 'base64').toString('utf8'); } + : _TD + ? function (a) { return _TD.decode(_toUint8Array(a)); } + : function (a) { return btou(_atob(a)); }; + var _unURI = function (a) { return _tidyB64(a.replace(/[-_]/g, function (m0) { return m0 == '-' ? '+' : '/'; })); }; + /** + * converts a Base64 string to a UTF-8 string. + * @param {String} src Base64 string. Both normal and URL-safe are supported + * @returns {string} UTF-8 string + */ + var decode = function (src) { return _decode(_unURI(src)); }; + /** + * check if a value is a valid Base64 string + * @param {String} src a value to check + */ + var isValid = function (src) { + if (typeof src !== 'string') + return false; + var s = src.replace(/\s+/g, '').replace(/={0,2}$/, ''); + return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s); + }; + // + var _noEnum = function (v) { + return { + value: v, enumerable: false, writable: true, configurable: true + }; + }; + /** + * extend String.prototype with relevant methods + */ + var extendString = function () { + var _add = function (name, body) { return Object.defineProperty(String.prototype, name, _noEnum(body)); }; + _add('fromBase64', function () { return decode(this); }); + _add('toBase64', function (urlsafe) { return encode(this, urlsafe); }); + _add('toBase64URI', function () { return encode(this, true); }); + _add('toBase64URL', function () { return encode(this, true); }); + _add('toUint8Array', function () { return toUint8Array(this); }); + }; + /** + * extend Uint8Array.prototype with relevant methods + */ + var extendUint8Array = function () { + var _add = function (name, body) { return Object.defineProperty(Uint8Array.prototype, name, _noEnum(body)); }; + _add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); }); + _add('toBase64URI', function () { return fromUint8Array(this, true); }); + _add('toBase64URL', function () { return fromUint8Array(this, true); }); + }; + /** + * extend Builtin prototypes with relevant methods + */ + var extendBuiltins = function () { + extendString(); + extendUint8Array(); + }; + var gBase64 = { + version: version, + VERSION: VERSION, + atob: _atob, + atobPolyfill: atobPolyfill, + btoa: _btoa, + btoaPolyfill: btoaPolyfill, + fromBase64: decode, + toBase64: encode, + encode: encode, + encodeURI: encodeURI, + encodeURL: encodeURI, + utob: utob, + btou: btou, + decode: decode, + isValid: isValid, + fromUint8Array: fromUint8Array, + toUint8Array: toUint8Array, + extendString: extendString, + extendUint8Array: extendUint8Array, + extendBuiltins: extendBuiltins + }; + // + // export Base64 to the namespace + // + // ES5 is yet to have Object.assign() that may make transpilers unhappy. + // gBase64.Base64 = Object.assign({}, gBase64); + gBase64.Base64 = {}; + Object.keys(gBase64).forEach(function (k) { return gBase64.Base64[k] = gBase64[k]; }); + return gBase64; +})); diff --git a/scripts/bech32 license.txt b/scripts/bech32 license.txt new file mode 100755 index 0000000..8ae7d39 --- /dev/null +++ b/scripts/bech32 license.txt @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2017 Pieter Wuille +Copyright (c) 2018 bitcoinjs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/bech32-2.0.0.js b/scripts/bech32-2.0.0.js new file mode 100755 index 0000000..268eef0 --- /dev/null +++ b/scripts/bech32-2.0.0.js @@ -0,0 +1,170 @@ +'use strict'; +//Object.defineProperty(globalThis, "__esModule", { value: true }); +globalThis.bech32m = globalThis.bech32 = void 0; +const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; +const ALPHABET_MAP = {}; +for (let z = 0; z < ALPHABET.length; z++) { + const x = ALPHABET.charAt(z); + ALPHABET_MAP[x] = z; +} +function polymodStep(pre) { + const b = pre >> 25; + return (((pre & 0x1ffffff) << 5) ^ + (-((b >> 0) & 1) & 0x3b6a57b2) ^ + (-((b >> 1) & 1) & 0x26508e6d) ^ + (-((b >> 2) & 1) & 0x1ea119fa) ^ + (-((b >> 3) & 1) & 0x3d4233dd) ^ + (-((b >> 4) & 1) & 0x2a1462b3)); +} +function prefixChk(prefix) { + let chk = 1; + for (let i = 0; i < prefix.length; ++i) { + const c = prefix.charCodeAt(i); + if (c < 33 || c > 126) + return 'Invalid prefix (' + prefix + ')'; + chk = polymodStep(chk) ^ (c >> 5); + } + chk = polymodStep(chk); + for (let i = 0; i < prefix.length; ++i) { + const v = prefix.charCodeAt(i); + chk = polymodStep(chk) ^ (v & 0x1f); + } + return chk; +} +function convert(data, inBits, outBits, pad) { + let value = 0; + let bits = 0; + const maxV = (1 << outBits) - 1; + const result = []; + for (let i = 0; i < data.length; ++i) { + value = (value << inBits) | data[i]; + bits += inBits; + while (bits >= outBits) { + bits -= outBits; + result.push((value >> bits) & maxV); + } + } + if (pad) { + if (bits > 0) { + result.push((value << (outBits - bits)) & maxV); + } + } + else { + if (bits >= inBits) + return 'Excess padding'; + if ((value << (outBits - bits)) & maxV) + return 'Non-zero padding'; + } + return result; +} +function toWords(bytes) { + return convert(bytes, 8, 5, true); +} +function fromWordsUnsafe(words) { + const res = convert(words, 5, 8, false); + if (Array.isArray(res)) + return res; +} +function fromWords(words) { + const res = convert(words, 5, 8, false); + if (Array.isArray(res)) + return res; + throw new Error(res); +} +function getLibraryFromEncoding(encoding) { + let ENCODING_CONST; + if (encoding === 'bech32') { + ENCODING_CONST = 1; + } + else { + ENCODING_CONST = 0x2bc830a3; + } + function encode(prefix, words, LIMIT) { + LIMIT = LIMIT || 90; + if (prefix.length + 7 + words.length > LIMIT) + throw new TypeError('Exceeds length limit'); + prefix = prefix.toLowerCase(); + // determine chk mod + let chk = prefixChk(prefix); + if (typeof chk === 'string') + throw new Error(chk); + let result = prefix + '1'; + for (let i = 0; i < words.length; ++i) { + const x = words[i]; + if (x >> 5 !== 0) + throw new Error('Non 5-bit word'); + chk = polymodStep(chk) ^ x; + result += ALPHABET.charAt(x); + } + for (let i = 0; i < 6; ++i) { + chk = polymodStep(chk); + } + chk ^= ENCODING_CONST; + for (let i = 0; i < 6; ++i) { + const v = (chk >> ((5 - i) * 5)) & 0x1f; + result += ALPHABET.charAt(v); + } + return result; + } + function __decode(str, LIMIT) { + LIMIT = LIMIT || 90; + if (str.length < 8) + return str + ' too short'; + if (str.length > LIMIT) + return 'Exceeds length limit'; + // don't allow mixed case + const lowered = str.toLowerCase(); + const uppered = str.toUpperCase(); + if (str !== lowered && str !== uppered) + return 'Mixed-case string ' + str; + str = lowered; + const split = str.lastIndexOf('1'); + if (split === -1) + return 'No separator character for ' + str; + if (split === 0) + return 'Missing prefix for ' + str; + const prefix = str.slice(0, split); + const wordChars = str.slice(split + 1); + if (wordChars.length < 6) + return 'Data too short'; + let chk = prefixChk(prefix); + if (typeof chk === 'string') + return chk; + const words = []; + for (let i = 0; i < wordChars.length; ++i) { + const c = wordChars.charAt(i); + const v = ALPHABET_MAP[c]; + if (v === undefined) + return 'Unknown character ' + c; + chk = polymodStep(chk) ^ v; + // not in the checksum? + if (i + 6 >= wordChars.length) + continue; + words.push(v); + } + if (chk !== ENCODING_CONST) + return 'Invalid checksum for ' + str; + return { prefix, words }; + } + function decodeUnsafe(str, LIMIT) { + const res = __decode(str, LIMIT); + if (typeof res === 'object') + return res; + } + function decode(str, LIMIT) { + const res = __decode(str, LIMIT); + if (typeof res === 'object') + return res; + throw new Error(res); + } + return { + decodeUnsafe, + decode, + encode, + toWords, + fromWordsUnsafe, + fromWords, + }; +} +globalThis.bech32 = getLibraryFromEncoding('bech32'); +globalThis.bech32m = getLibraryFromEncoding('bech32m'); diff --git a/scripts/bignumber.js license.txt b/scripts/bignumber.js license.txt new file mode 100755 index 0000000..83b6c7c --- /dev/null +++ b/scripts/bignumber.js license.txt @@ -0,0 +1,26 @@ +The MIT License (MIT) +===================== + +Copyright © `<2022>` `Michael Mclaughlin` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/scripts/bignumber.js-9.1.1.js b/scripts/bignumber.js-9.1.1.js new file mode 100755 index 0000000..da6d84b --- /dev/null +++ b/scripts/bignumber.js-9.1.1.js @@ -0,0 +1,2926 @@ +;(function (globalObject) { + 'use strict'; + +/* + * bignumber.js v9.1.1 + * A JavaScript library for arbitrary-precision arithmetic. + * https://github.com/MikeMcl/bignumber.js + * Copyright (c) 2022 Michael Mclaughlin + * MIT Licensed. + * + * BigNumber.prototype methods | BigNumber methods + * | + * absoluteValue abs | clone + * comparedTo | config set + * decimalPlaces dp | DECIMAL_PLACES + * dividedBy div | ROUNDING_MODE + * dividedToIntegerBy idiv | EXPONENTIAL_AT + * exponentiatedBy pow | RANGE + * integerValue | CRYPTO + * isEqualTo eq | MODULO_MODE + * isFinite | POW_PRECISION + * isGreaterThan gt | FORMAT + * isGreaterThanOrEqualTo gte | ALPHABET + * isInteger | isBigNumber + * isLessThan lt | maximum max + * isLessThanOrEqualTo lte | minimum min + * isNaN | random + * isNegative | sum + * isPositive | + * isZero | + * minus | + * modulo mod | + * multipliedBy times | + * negated | + * plus | + * precision sd | + * shiftedBy | + * squareRoot sqrt | + * toExponential | + * toFixed | + * toFormat | + * toFraction | + * toJSON | + * toNumber | + * toPrecision | + * toString | + * valueOf | + * + */ + + + var BigNumber, + isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, + mathceil = Math.ceil, + mathfloor = Math.floor, + + bignumberError = '[BigNumber Error] ', + tooManyDigits = bignumberError + 'Number primitive has more than 15 significant digits: ', + + BASE = 1e14, + LOG_BASE = 14, + MAX_SAFE_INTEGER = 0x1fffffffffffff, // 2^53 - 1 + // MAX_INT32 = 0x7fffffff, // 2^31 - 1 + POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], + SQRT_BASE = 1e7, + + // EDITABLE + // The limit on the value of DECIMAL_PLACES, TO_EXP_NEG, TO_EXP_POS, MIN_EXP, MAX_EXP, and + // the arguments to toExponential, toFixed, toFormat, and toPrecision. + MAX = 1E9; // 0 to MAX_INT32 + + + /* + * Create and return a BigNumber constructor. + */ + function clone(configObject) { + var div, convertBase, parseNumeric, + P = BigNumber.prototype = { constructor: BigNumber, toString: null, valueOf: null }, + ONE = new BigNumber(1), + + + //----------------------------- EDITABLE CONFIG DEFAULTS ------------------------------- + + + // The default values below must be integers within the inclusive ranges stated. + // The values can also be changed at run-time using BigNumber.set. + + // The maximum number of decimal places for operations involving division. + DECIMAL_PLACES = 20, // 0 to MAX + + // The rounding mode used when rounding to the above decimal places, and when using + // toExponential, toFixed, toFormat and toPrecision, and round (default value). + // UP 0 Away from zero. + // DOWN 1 Towards zero. + // CEIL 2 Towards +Infinity. + // FLOOR 3 Towards -Infinity. + // HALF_UP 4 Towards nearest neighbour. If equidistant, up. + // HALF_DOWN 5 Towards nearest neighbour. If equidistant, down. + // HALF_EVEN 6 Towards nearest neighbour. If equidistant, towards even neighbour. + // HALF_CEIL 7 Towards nearest neighbour. If equidistant, towards +Infinity. + // HALF_FLOOR 8 Towards nearest neighbour. If equidistant, towards -Infinity. + ROUNDING_MODE = 4, // 0 to 8 + + // EXPONENTIAL_AT : [TO_EXP_NEG , TO_EXP_POS] + + // The exponent value at and beneath which toString returns exponential notation. + // Number type: -7 + TO_EXP_NEG = -7, // 0 to -MAX + + // The exponent value at and above which toString returns exponential notation. + // Number type: 21 + TO_EXP_POS = 21, // 0 to MAX + + // RANGE : [MIN_EXP, MAX_EXP] + + // The minimum exponent value, beneath which underflow to zero occurs. + // Number type: -324 (5e-324) + MIN_EXP = -1e7, // -1 to -MAX + + // The maximum exponent value, above which overflow to Infinity occurs. + // Number type: 308 (1.7976931348623157e+308) + // For MAX_EXP > 1e7, e.g. new BigNumber('1e100000000').plus(1) may be slow. + MAX_EXP = 1e7, // 1 to MAX + + // Whether to use cryptographically-secure random number generation, if available. + CRYPTO = false, // true or false + + // The modulo mode used when calculating the modulus: a mod n. + // The quotient (q = a / n) is calculated according to the corresponding rounding mode. + // The remainder (r) is calculated as: r = a - n * q. + // + // UP 0 The remainder is positive if the dividend is negative, else is negative. + // DOWN 1 The remainder has the same sign as the dividend. + // This modulo mode is commonly known as 'truncated division' and is + // equivalent to (a % n) in JavaScript. + // FLOOR 3 The remainder has the same sign as the divisor (Python %). + // HALF_EVEN 6 This modulo mode implements the IEEE 754 remainder function. + // EUCLID 9 Euclidian division. q = sign(n) * floor(a / abs(n)). + // The remainder is always positive. + // + // The truncated division, floored division, Euclidian division and IEEE 754 remainder + // modes are commonly used for the modulus operation. + // Although the other rounding modes can also be used, they may not give useful results. + MODULO_MODE = 1, // 0 to 9 + + // The maximum number of significant digits of the result of the exponentiatedBy operation. + // If POW_PRECISION is 0, there will be unlimited significant digits. + POW_PRECISION = 0, // 0 to MAX + + // The format specification used by the BigNumber.prototype.toFormat method. + FORMAT = { + prefix: '', + groupSize: 3, + secondaryGroupSize: 0, + groupSeparator: ',', + decimalSeparator: '.', + fractionGroupSize: 0, + fractionGroupSeparator: '\xA0', // non-breaking space + suffix: '' + }, + + // The alphabet used for base conversion. It must be at least 2 characters long, with no '+', + // '-', '.', whitespace, or repeated character. + // '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_' + ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz', + alphabetHasNormalDecimalDigits = true; + + + //------------------------------------------------------------------------------------------ + + + // CONSTRUCTOR + + + /* + * The BigNumber constructor and exported function. + * Create and return a new instance of a BigNumber object. + * + * v {number|string|BigNumber} A numeric value. + * [b] {number} The base of v. Integer, 2 to ALPHABET.length inclusive. + */ + function BigNumber(v, b) { + var alphabet, c, caseChanged, e, i, isNum, len, str, + x = this; + + // Enable constructor call without `new`. + if (!(x instanceof BigNumber)) return new BigNumber(v, b); + + if (b == null) { + + if (v && v._isBigNumber === true) { + x.s = v.s; + + if (!v.c || v.e > MAX_EXP) { + x.c = x.e = null; + } else if (v.e < MIN_EXP) { + x.c = [x.e = 0]; + } else { + x.e = v.e; + x.c = v.c.slice(); + } + + return; + } + + if ((isNum = typeof v == 'number') && v * 0 == 0) { + + // Use `1 / n` to handle minus zero also. + x.s = 1 / v < 0 ? (v = -v, -1) : 1; + + // Fast path for integers, where n < 2147483648 (2**31). + if (v === ~~v) { + for (e = 0, i = v; i >= 10; i /= 10, e++); + + if (e > MAX_EXP) { + x.c = x.e = null; + } else { + x.e = e; + x.c = [v]; + } + + return; + } + + str = String(v); + } else { + + if (!isNumeric.test(str = String(v))) return parseNumeric(x, str, isNum); + + x.s = str.charCodeAt(0) == 45 ? (str = str.slice(1), -1) : 1; + } + + // Decimal point? + if ((e = str.indexOf('.')) > -1) str = str.replace('.', ''); + + // Exponential form? + if ((i = str.search(/e/i)) > 0) { + + // Determine exponent. + if (e < 0) e = i; + e += +str.slice(i + 1); + str = str.substring(0, i); + } else if (e < 0) { + + // Integer. + e = str.length; + } + + } else { + + // '[BigNumber Error] Base {not a primitive number|not an integer|out of range}: {b}' + intCheck(b, 2, ALPHABET.length, 'Base'); + + // Allow exponential notation to be used with base 10 argument, while + // also rounding to DECIMAL_PLACES as with other bases. + if (b == 10 && alphabetHasNormalDecimalDigits) { + x = new BigNumber(v); + return round(x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE); + } + + str = String(v); + + if (isNum = typeof v == 'number') { + + // Avoid potential interpretation of Infinity and NaN as base 44+ values. + if (v * 0 != 0) return parseNumeric(x, str, isNum, b); + + x.s = 1 / v < 0 ? (str = str.slice(1), -1) : 1; + + // '[BigNumber Error] Number primitive has more than 15 significant digits: {n}' + if (BigNumber.DEBUG && str.replace(/^0\.0*|\./, '').length > 15) { + throw Error + (tooManyDigits + v); + } + } else { + x.s = str.charCodeAt(0) === 45 ? (str = str.slice(1), -1) : 1; + } + + alphabet = ALPHABET.slice(0, b); + e = i = 0; + + // Check that str is a valid base b number. + // Don't use RegExp, so alphabet can contain special characters. + for (len = str.length; i < len; i++) { + if (alphabet.indexOf(c = str.charAt(i)) < 0) { + if (c == '.') { + + // If '.' is not the first character and it has not be found before. + if (i > e) { + e = len; + continue; + } + } else if (!caseChanged) { + + // Allow e.g. hexadecimal 'FF' as well as 'ff'. + if (str == str.toUpperCase() && (str = str.toLowerCase()) || + str == str.toLowerCase() && (str = str.toUpperCase())) { + caseChanged = true; + i = -1; + e = 0; + continue; + } + } + + return parseNumeric(x, String(v), isNum, b); + } + } + + // Prevent later check for length on converted number. + isNum = false; + str = convertBase(str, b, 10, x.s); + + // Decimal point? + if ((e = str.indexOf('.')) > -1) str = str.replace('.', ''); + else e = str.length; + } + + // Determine leading zeros. + for (i = 0; str.charCodeAt(i) === 48; i++); + + // Determine trailing zeros. + for (len = str.length; str.charCodeAt(--len) === 48;); + + if (str = str.slice(i, ++len)) { + len -= i; + + // '[BigNumber Error] Number primitive has more than 15 significant digits: {n}' + if (isNum && BigNumber.DEBUG && + len > 15 && (v > MAX_SAFE_INTEGER || v !== mathfloor(v))) { + throw Error + (tooManyDigits + (x.s * v)); + } + + // Overflow? + if ((e = e - i - 1) > MAX_EXP) { + + // Infinity. + x.c = x.e = null; + + // Underflow? + } else if (e < MIN_EXP) { + + // Zero. + x.c = [x.e = 0]; + } else { + x.e = e; + x.c = []; + + // Transform base + + // e is the base 10 exponent. + // i is where to slice str to get the first element of the coefficient array. + i = (e + 1) % LOG_BASE; + if (e < 0) i += LOG_BASE; // i < 1 + + if (i < len) { + if (i) x.c.push(+str.slice(0, i)); + + for (len -= LOG_BASE; i < len;) { + x.c.push(+str.slice(i, i += LOG_BASE)); + } + + i = LOG_BASE - (str = str.slice(i)).length; + } else { + i -= len; + } + + for (; i--; str += '0'); + x.c.push(+str); + } + } else { + + // Zero. + x.c = [x.e = 0]; + } + } + + + // CONSTRUCTOR PROPERTIES + + + BigNumber.clone = clone; + + BigNumber.ROUND_UP = 0; + BigNumber.ROUND_DOWN = 1; + BigNumber.ROUND_CEIL = 2; + BigNumber.ROUND_FLOOR = 3; + BigNumber.ROUND_HALF_UP = 4; + BigNumber.ROUND_HALF_DOWN = 5; + BigNumber.ROUND_HALF_EVEN = 6; + BigNumber.ROUND_HALF_CEIL = 7; + BigNumber.ROUND_HALF_FLOOR = 8; + BigNumber.EUCLID = 9; + + + /* + * Configure infrequently-changing library-wide settings. + * + * Accept an object with the following optional properties (if the value of a property is + * a number, it must be an integer within the inclusive range stated): + * + * DECIMAL_PLACES {number} 0 to MAX + * ROUNDING_MODE {number} 0 to 8 + * EXPONENTIAL_AT {number|number[]} -MAX to MAX or [-MAX to 0, 0 to MAX] + * RANGE {number|number[]} -MAX to MAX (not zero) or [-MAX to -1, 1 to MAX] + * CRYPTO {boolean} true or false + * MODULO_MODE {number} 0 to 9 + * POW_PRECISION {number} 0 to MAX + * ALPHABET {string} A string of two or more unique characters which does + * not contain '.'. + * FORMAT {object} An object with some of the following properties: + * prefix {string} + * groupSize {number} + * secondaryGroupSize {number} + * groupSeparator {string} + * decimalSeparator {string} + * fractionGroupSize {number} + * fractionGroupSeparator {string} + * suffix {string} + * + * (The values assigned to the above FORMAT object properties are not checked for validity.) + * + * E.g. + * BigNumber.config({ DECIMAL_PLACES : 20, ROUNDING_MODE : 4 }) + * + * Ignore properties/parameters set to null or undefined, except for ALPHABET. + * + * Return an object with the properties current values. + */ + BigNumber.config = BigNumber.set = function (obj) { + var p, v; + + if (obj != null) { + + if (typeof obj == 'object') { + + // DECIMAL_PLACES {number} Integer, 0 to MAX inclusive. + // '[BigNumber Error] DECIMAL_PLACES {not a primitive number|not an integer|out of range}: {v}' + if (obj.hasOwnProperty(p = 'DECIMAL_PLACES')) { + v = obj[p]; + intCheck(v, 0, MAX, p); + DECIMAL_PLACES = v; + } + + // ROUNDING_MODE {number} Integer, 0 to 8 inclusive. + // '[BigNumber Error] ROUNDING_MODE {not a primitive number|not an integer|out of range}: {v}' + if (obj.hasOwnProperty(p = 'ROUNDING_MODE')) { + v = obj[p]; + intCheck(v, 0, 8, p); + ROUNDING_MODE = v; + } + + // EXPONENTIAL_AT {number|number[]} + // Integer, -MAX to MAX inclusive or + // [integer -MAX to 0 inclusive, 0 to MAX inclusive]. + // '[BigNumber Error] EXPONENTIAL_AT {not a primitive number|not an integer|out of range}: {v}' + if (obj.hasOwnProperty(p = 'EXPONENTIAL_AT')) { + v = obj[p]; + if (v && v.pop) { + intCheck(v[0], -MAX, 0, p); + intCheck(v[1], 0, MAX, p); + TO_EXP_NEG = v[0]; + TO_EXP_POS = v[1]; + } else { + intCheck(v, -MAX, MAX, p); + TO_EXP_NEG = -(TO_EXP_POS = v < 0 ? -v : v); + } + } + + // RANGE {number|number[]} Non-zero integer, -MAX to MAX inclusive or + // [integer -MAX to -1 inclusive, integer 1 to MAX inclusive]. + // '[BigNumber Error] RANGE {not a primitive number|not an integer|out of range|cannot be zero}: {v}' + if (obj.hasOwnProperty(p = 'RANGE')) { + v = obj[p]; + if (v && v.pop) { + intCheck(v[0], -MAX, -1, p); + intCheck(v[1], 1, MAX, p); + MIN_EXP = v[0]; + MAX_EXP = v[1]; + } else { + intCheck(v, -MAX, MAX, p); + if (v) { + MIN_EXP = -(MAX_EXP = v < 0 ? -v : v); + } else { + throw Error + (bignumberError + p + ' cannot be zero: ' + v); + } + } + } + + // CRYPTO {boolean} true or false. + // '[BigNumber Error] CRYPTO not true or false: {v}' + // '[BigNumber Error] crypto unavailable' + if (obj.hasOwnProperty(p = 'CRYPTO')) { + v = obj[p]; + if (v === !!v) { + if (v) { + if (typeof crypto != 'undefined' && crypto && + (crypto.getRandomValues || crypto.randomBytes)) { + CRYPTO = v; + } else { + CRYPTO = !v; + throw Error + (bignumberError + 'crypto unavailable'); + } + } else { + CRYPTO = v; + } + } else { + throw Error + (bignumberError + p + ' not true or false: ' + v); + } + } + + // MODULO_MODE {number} Integer, 0 to 9 inclusive. + // '[BigNumber Error] MODULO_MODE {not a primitive number|not an integer|out of range}: {v}' + if (obj.hasOwnProperty(p = 'MODULO_MODE')) { + v = obj[p]; + intCheck(v, 0, 9, p); + MODULO_MODE = v; + } + + // POW_PRECISION {number} Integer, 0 to MAX inclusive. + // '[BigNumber Error] POW_PRECISION {not a primitive number|not an integer|out of range}: {v}' + if (obj.hasOwnProperty(p = 'POW_PRECISION')) { + v = obj[p]; + intCheck(v, 0, MAX, p); + POW_PRECISION = v; + } + + // FORMAT {object} + // '[BigNumber Error] FORMAT not an object: {v}' + if (obj.hasOwnProperty(p = 'FORMAT')) { + v = obj[p]; + if (typeof v == 'object') FORMAT = v; + else throw Error + (bignumberError + p + ' not an object: ' + v); + } + + // ALPHABET {string} + // '[BigNumber Error] ALPHABET invalid: {v}' + if (obj.hasOwnProperty(p = 'ALPHABET')) { + v = obj[p]; + + // Disallow if less than two characters, + // or if it contains '+', '-', '.', whitespace, or a repeated character. + if (typeof v == 'string' && !/^.?$|[+\-.\s]|(.).*\1/.test(v)) { + alphabetHasNormalDecimalDigits = v.slice(0, 10) == '0123456789'; + ALPHABET = v; + } else { + throw Error + (bignumberError + p + ' invalid: ' + v); + } + } + + } else { + + // '[BigNumber Error] Object expected: {v}' + throw Error + (bignumberError + 'Object expected: ' + obj); + } + } + + return { + DECIMAL_PLACES: DECIMAL_PLACES, + ROUNDING_MODE: ROUNDING_MODE, + EXPONENTIAL_AT: [TO_EXP_NEG, TO_EXP_POS], + RANGE: [MIN_EXP, MAX_EXP], + CRYPTO: CRYPTO, + MODULO_MODE: MODULO_MODE, + POW_PRECISION: POW_PRECISION, + FORMAT: FORMAT, + ALPHABET: ALPHABET + }; + }; + + + /* + * Return true if v is a BigNumber instance, otherwise return false. + * + * If BigNumber.DEBUG is true, throw if a BigNumber instance is not well-formed. + * + * v {any} + * + * '[BigNumber Error] Invalid BigNumber: {v}' + */ + BigNumber.isBigNumber = function (v) { + if (!v || v._isBigNumber !== true) return false; + if (!BigNumber.DEBUG) return true; + + var i, n, + c = v.c, + e = v.e, + s = v.s; + + out: if ({}.toString.call(c) == '[object Array]') { + + if ((s === 1 || s === -1) && e >= -MAX && e <= MAX && e === mathfloor(e)) { + + // If the first element is zero, the BigNumber value must be zero. + if (c[0] === 0) { + if (e === 0 && c.length === 1) return true; + break out; + } + + // Calculate number of digits that c[0] should have, based on the exponent. + i = (e + 1) % LOG_BASE; + if (i < 1) i += LOG_BASE; + + // Calculate number of digits of c[0]. + //if (Math.ceil(Math.log(c[0] + 1) / Math.LN10) == i) { + if (String(c[0]).length == i) { + + for (i = 0; i < c.length; i++) { + n = c[i]; + if (n < 0 || n >= BASE || n !== mathfloor(n)) break out; + } + + // Last element cannot be zero, unless it is the only element. + if (n !== 0) return true; + } + } + + // Infinity/NaN + } else if (c === null && e === null && (s === null || s === 1 || s === -1)) { + return true; + } + + throw Error + (bignumberError + 'Invalid BigNumber: ' + v); + }; + + + /* + * Return a new BigNumber whose value is the maximum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.maximum = BigNumber.max = function () { + return maxOrMin(arguments, P.lt); + }; + + + /* + * Return a new BigNumber whose value is the minimum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.minimum = BigNumber.min = function () { + return maxOrMin(arguments, P.gt); + }; + + + /* + * Return a new BigNumber with a random value equal to or greater than 0 and less than 1, + * and with dp, or DECIMAL_PLACES if dp is omitted, decimal places (or less if trailing + * zeros are produced). + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp}' + * '[BigNumber Error] crypto unavailable' + */ + BigNumber.random = (function () { + var pow2_53 = 0x20000000000000; + + // Return a 53 bit integer n, where 0 <= n < 9007199254740992. + // Check if Math.random() produces more than 32 bits of randomness. + // If it does, assume at least 53 bits are produced, otherwise assume at least 30 bits. + // 0x40000000 is 2^30, 0x800000 is 2^23, 0x1fffff is 2^21 - 1. + var random53bitInt = (Math.random() * pow2_53) & 0x1fffff + ? function () { return mathfloor(Math.random() * pow2_53); } + : function () { return ((Math.random() * 0x40000000 | 0) * 0x800000) + + (Math.random() * 0x800000 | 0); }; + + return function (dp) { + var a, b, e, k, v, + i = 0, + c = [], + rand = new BigNumber(ONE); + + if (dp == null) dp = DECIMAL_PLACES; + else intCheck(dp, 0, MAX); + + k = mathceil(dp / LOG_BASE); + + if (CRYPTO) { + + // Browsers supporting crypto.getRandomValues. + if (crypto.getRandomValues) { + + a = crypto.getRandomValues(new Uint32Array(k *= 2)); + + for (; i < k;) { + + // 53 bits: + // ((Math.pow(2, 32) - 1) * Math.pow(2, 21)).toString(2) + // 11111 11111111 11111111 11111111 11100000 00000000 00000000 + // ((Math.pow(2, 32) - 1) >>> 11).toString(2) + // 11111 11111111 11111111 + // 0x20000 is 2^21. + v = a[i] * 0x20000 + (a[i + 1] >>> 11); + + // Rejection sampling: + // 0 <= v < 9007199254740992 + // Probability that v >= 9e15, is + // 7199254740992 / 9007199254740992 ~= 0.0008, i.e. 1 in 1251 + if (v >= 9e15) { + b = crypto.getRandomValues(new Uint32Array(2)); + a[i] = b[0]; + a[i + 1] = b[1]; + } else { + + // 0 <= v <= 8999999999999999 + // 0 <= (v % 1e14) <= 99999999999999 + c.push(v % 1e14); + i += 2; + } + } + i = k / 2; + + // Node.js supporting crypto.randomBytes. + } else if (crypto.randomBytes) { + + // buffer + a = crypto.randomBytes(k *= 7); + + for (; i < k;) { + + // 0x1000000000000 is 2^48, 0x10000000000 is 2^40 + // 0x100000000 is 2^32, 0x1000000 is 2^24 + // 11111 11111111 11111111 11111111 11111111 11111111 11111111 + // 0 <= v < 9007199254740992 + v = ((a[i] & 31) * 0x1000000000000) + (a[i + 1] * 0x10000000000) + + (a[i + 2] * 0x100000000) + (a[i + 3] * 0x1000000) + + (a[i + 4] << 16) + (a[i + 5] << 8) + a[i + 6]; + + if (v >= 9e15) { + crypto.randomBytes(7).copy(a, i); + } else { + + // 0 <= (v % 1e14) <= 99999999999999 + c.push(v % 1e14); + i += 7; + } + } + i = k / 7; + } else { + CRYPTO = false; + throw Error + (bignumberError + 'crypto unavailable'); + } + } + + // Use Math.random. + if (!CRYPTO) { + + for (; i < k;) { + v = random53bitInt(); + if (v < 9e15) c[i++] = v % 1e14; + } + } + + k = c[--i]; + dp %= LOG_BASE; + + // Convert trailing digits to zeros according to dp. + if (k && dp) { + v = POWS_TEN[LOG_BASE - dp]; + c[i] = mathfloor(k / v) * v; + } + + // Remove trailing elements which are zero. + for (; c[i] === 0; c.pop(), i--); + + // Zero? + if (i < 0) { + c = [e = 0]; + } else { + + // Remove leading elements which are zero and adjust exponent accordingly. + for (e = -1 ; c[0] === 0; c.splice(0, 1), e -= LOG_BASE); + + // Count the digits of the first element of c to determine leading zeros, and... + for (i = 1, v = c[0]; v >= 10; v /= 10, i++); + + // adjust the exponent accordingly. + if (i < LOG_BASE) e -= LOG_BASE - i; + } + + rand.e = e; + rand.c = c; + return rand; + }; + })(); + + + /* + * Return a BigNumber whose value is the sum of the arguments. + * + * arguments {number|string|BigNumber} + */ + BigNumber.sum = function () { + var i = 1, + args = arguments, + sum = new BigNumber(args[0]); + for (; i < args.length;) sum = sum.plus(args[i++]); + return sum; + }; + + + // PRIVATE FUNCTIONS + + + // Called by BigNumber and BigNumber.prototype.toString. + convertBase = (function () { + var decimal = '0123456789'; + + /* + * Convert string of baseIn to an array of numbers of baseOut. + * Eg. toBaseOut('255', 10, 16) returns [15, 15]. + * Eg. toBaseOut('ff', 16, 10) returns [2, 5, 5]. + */ + function toBaseOut(str, baseIn, baseOut, alphabet) { + var j, + arr = [0], + arrL, + i = 0, + len = str.length; + + for (; i < len;) { + for (arrL = arr.length; arrL--; arr[arrL] *= baseIn); + + arr[0] += alphabet.indexOf(str.charAt(i++)); + + for (j = 0; j < arr.length; j++) { + + if (arr[j] > baseOut - 1) { + if (arr[j + 1] == null) arr[j + 1] = 0; + arr[j + 1] += arr[j] / baseOut | 0; + arr[j] %= baseOut; + } + } + } + + return arr.reverse(); + } + + // Convert a numeric string of baseIn to a numeric string of baseOut. + // If the caller is toString, we are converting from base 10 to baseOut. + // If the caller is BigNumber, we are converting from baseIn to base 10. + return function (str, baseIn, baseOut, sign, callerIsToString) { + var alphabet, d, e, k, r, x, xc, y, + i = str.indexOf('.'), + dp = DECIMAL_PLACES, + rm = ROUNDING_MODE; + + // Non-integer. + if (i >= 0) { + k = POW_PRECISION; + + // Unlimited precision. + POW_PRECISION = 0; + str = str.replace('.', ''); + y = new BigNumber(baseIn); + x = y.pow(str.length - i); + POW_PRECISION = k; + + // Convert str as if an integer, then restore the fraction part by dividing the + // result by its base raised to a power. + + y.c = toBaseOut(toFixedPoint(coeffToString(x.c), x.e, '0'), + 10, baseOut, decimal); + y.e = y.c.length; + } + + // Convert the number as integer. + + xc = toBaseOut(str, baseIn, baseOut, callerIsToString + ? (alphabet = ALPHABET, decimal) + : (alphabet = decimal, ALPHABET)); + + // xc now represents str as an integer and converted to baseOut. e is the exponent. + e = k = xc.length; + + // Remove trailing zeros. + for (; xc[--k] == 0; xc.pop()); + + // Zero? + if (!xc[0]) return alphabet.charAt(0); + + // Does str represent an integer? If so, no need for the division. + if (i < 0) { + --e; + } else { + x.c = xc; + x.e = e; + + // The sign is needed for correct rounding. + x.s = sign; + x = div(x, y, dp, rm, baseOut); + xc = x.c; + r = x.r; + e = x.e; + } + + // xc now represents str converted to baseOut. + + // THe index of the rounding digit. + d = e + dp + 1; + + // The rounding digit: the digit to the right of the digit that may be rounded up. + i = xc[d]; + + // Look at the rounding digits and mode to determine whether to round up. + + k = baseOut / 2; + r = r || d < 0 || xc[d + 1] != null; + + r = rm < 4 ? (i != null || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) + : i > k || i == k &&(rm == 4 || r || rm == 6 && xc[d - 1] & 1 || + rm == (x.s < 0 ? 8 : 7)); + + // If the index of the rounding digit is not greater than zero, or xc represents + // zero, then the result of the base conversion is zero or, if rounding up, a value + // such as 0.00001. + if (d < 1 || !xc[0]) { + + // 1^-dp or 0 + str = r ? toFixedPoint(alphabet.charAt(1), -dp, alphabet.charAt(0)) : alphabet.charAt(0); + } else { + + // Truncate xc to the required number of decimal places. + xc.length = d; + + // Round up? + if (r) { + + // Rounding up may mean the previous digit has to be rounded up and so on. + for (--baseOut; ++xc[--d] > baseOut;) { + xc[d] = 0; + + if (!d) { + ++e; + xc = [1].concat(xc); + } + } + } + + // Determine trailing zeros. + for (k = xc.length; !xc[--k];); + + // E.g. [4, 11, 15] becomes 4bf. + for (i = 0, str = ''; i <= k; str += alphabet.charAt(xc[i++])); + + // Add leading zeros, decimal point and trailing zeros as required. + str = toFixedPoint(str, e, alphabet.charAt(0)); + } + + // The caller will add the sign. + return str; + }; + })(); + + + // Perform division in the specified base. Called by div and convertBase. + div = (function () { + + // Assume non-zero x and k. + function multiply(x, k, base) { + var m, temp, xlo, xhi, + carry = 0, + i = x.length, + klo = k % SQRT_BASE, + khi = k / SQRT_BASE | 0; + + for (x = x.slice(); i--;) { + xlo = x[i] % SQRT_BASE; + xhi = x[i] / SQRT_BASE | 0; + m = khi * xlo + xhi * klo; + temp = klo * xlo + ((m % SQRT_BASE) * SQRT_BASE) + carry; + carry = (temp / base | 0) + (m / SQRT_BASE | 0) + khi * xhi; + x[i] = temp % base; + } + + if (carry) x = [carry].concat(x); + + return x; + } + + function compare(a, b, aL, bL) { + var i, cmp; + + if (aL != bL) { + cmp = aL > bL ? 1 : -1; + } else { + + for (i = cmp = 0; i < aL; i++) { + + if (a[i] != b[i]) { + cmp = a[i] > b[i] ? 1 : -1; + break; + } + } + } + + return cmp; + } + + function subtract(a, b, aL, base) { + var i = 0; + + // Subtract b from a. + for (; aL--;) { + a[aL] -= i; + i = a[aL] < b[aL] ? 1 : 0; + a[aL] = i * base + a[aL] - b[aL]; + } + + // Remove leading zeros. + for (; !a[0] && a.length > 1; a.splice(0, 1)); + } + + // x: dividend, y: divisor. + return function (x, y, dp, rm, base) { + var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, + yL, yz, + s = x.s == y.s ? 1 : -1, + xc = x.c, + yc = y.c; + + // Either NaN, Infinity or 0? + if (!xc || !xc[0] || !yc || !yc[0]) { + + return new BigNumber( + + // Return NaN if either NaN, or both Infinity or 0. + !x.s || !y.s || (xc ? yc && xc[0] == yc[0] : !yc) ? NaN : + + // Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0. + xc && xc[0] == 0 || !yc ? s * 0 : s / 0 + ); + } + + q = new BigNumber(s); + qc = q.c = []; + e = x.e - y.e; + s = dp + e + 1; + + if (!base) { + base = BASE; + e = bitFloor(x.e / LOG_BASE) - bitFloor(y.e / LOG_BASE); + s = s / LOG_BASE | 0; + } + + // Result exponent may be one less then the current value of e. + // The coefficients of the BigNumbers from convertBase may have trailing zeros. + for (i = 0; yc[i] == (xc[i] || 0); i++); + + if (yc[i] > (xc[i] || 0)) e--; + + if (s < 0) { + qc.push(1); + more = true; + } else { + xL = xc.length; + yL = yc.length; + i = 0; + s += 2; + + // Normalise xc and yc so highest order digit of yc is >= base / 2. + + n = mathfloor(base / (yc[0] + 1)); + + // Not necessary, but to handle odd bases where yc[0] == (base / 2) - 1. + // if (n > 1 || n++ == 1 && yc[0] < base / 2) { + if (n > 1) { + yc = multiply(yc, n, base); + xc = multiply(xc, n, base); + yL = yc.length; + xL = xc.length; + } + + xi = yL; + rem = xc.slice(0, yL); + remL = rem.length; + + // Add zeros to make remainder as long as divisor. + for (; remL < yL; rem[remL++] = 0); + yz = yc.slice(); + yz = [0].concat(yz); + yc0 = yc[0]; + if (yc[1] >= base / 2) yc0++; + // Not necessary, but to prevent trial digit n > base, when using base 3. + // else if (base == 3 && yc0 == 1) yc0 = 1 + 1e-15; + + do { + n = 0; + + // Compare divisor and remainder. + cmp = compare(yc, rem, yL, remL); + + // If divisor < remainder. + if (cmp < 0) { + + // Calculate trial digit, n. + + rem0 = rem[0]; + if (yL != remL) rem0 = rem0 * base + (rem[1] || 0); + + // n is how many times the divisor goes into the current remainder. + n = mathfloor(rem0 / yc0); + + // Algorithm: + // product = divisor multiplied by trial digit (n). + // Compare product and remainder. + // If product is greater than remainder: + // Subtract divisor from product, decrement trial digit. + // Subtract product from remainder. + // If product was less than remainder at the last compare: + // Compare new remainder and divisor. + // If remainder is greater than divisor: + // Subtract divisor from remainder, increment trial digit. + + if (n > 1) { + + // n may be > base only when base is 3. + if (n >= base) n = base - 1; + + // product = divisor * trial digit. + prod = multiply(yc, n, base); + prodL = prod.length; + remL = rem.length; + + // Compare product and remainder. + // If product > remainder then trial digit n too high. + // n is 1 too high about 5% of the time, and is not known to have + // ever been more than 1 too high. + while (compare(prod, rem, prodL, remL) == 1) { + n--; + + // Subtract divisor from product. + subtract(prod, yL < prodL ? yz : yc, prodL, base); + prodL = prod.length; + cmp = 1; + } + } else { + + // n is 0 or 1, cmp is -1. + // If n is 0, there is no need to compare yc and rem again below, + // so change cmp to 1 to avoid it. + // If n is 1, leave cmp as -1, so yc and rem are compared again. + if (n == 0) { + + // divisor < remainder, so n must be at least 1. + cmp = n = 1; + } + + // product = divisor + prod = yc.slice(); + prodL = prod.length; + } + + if (prodL < remL) prod = [0].concat(prod); + + // Subtract product from remainder. + subtract(rem, prod, remL, base); + remL = rem.length; + + // If product was < remainder. + if (cmp == -1) { + + // Compare divisor and new remainder. + // If divisor < new remainder, subtract divisor from remainder. + // Trial digit n too low. + // n is 1 too low about 5% of the time, and very rarely 2 too low. + while (compare(yc, rem, yL, remL) < 1) { + n++; + + // Subtract divisor from remainder. + subtract(rem, yL < remL ? yz : yc, remL, base); + remL = rem.length; + } + } + } else if (cmp === 0) { + n++; + rem = [0]; + } // else cmp === 1 and n will be 0 + + // Add the next digit, n, to the result array. + qc[i++] = n; + + // Update the remainder. + if (rem[0]) { + rem[remL++] = xc[xi] || 0; + } else { + rem = [xc[xi]]; + remL = 1; + } + } while ((xi++ < xL || rem[0] != null) && s--); + + more = rem[0] != null; + + // Leading zero? + if (!qc[0]) qc.splice(0, 1); + } + + if (base == BASE) { + + // To calculate q.e, first get the number of digits of qc[0]. + for (i = 1, s = qc[0]; s >= 10; s /= 10, i++); + + round(q, dp + (q.e = i + e * LOG_BASE - 1) + 1, rm, more); + + // Caller is convertBase. + } else { + q.e = e; + q.r = +more; + } + + return q; + }; + })(); + + + /* + * Return a string representing the value of BigNumber n in fixed-point or exponential + * notation rounded to the specified decimal places or significant digits. + * + * n: a BigNumber. + * i: the index of the last digit required (i.e. the digit that may be rounded up). + * rm: the rounding mode. + * id: 1 (toExponential) or 2 (toPrecision). + */ + function format(n, i, rm, id) { + var c0, e, ne, len, str; + + if (rm == null) rm = ROUNDING_MODE; + else intCheck(rm, 0, 8); + + if (!n.c) return n.toString(); + + c0 = n.c[0]; + ne = n.e; + + if (i == null) { + str = coeffToString(n.c); + str = id == 1 || id == 2 && (ne <= TO_EXP_NEG || ne >= TO_EXP_POS) + ? toExponential(str, ne) + : toFixedPoint(str, ne, '0'); + } else { + n = round(new BigNumber(n), i, rm); + + // n.e may have changed if the value was rounded up. + e = n.e; + + str = coeffToString(n.c); + len = str.length; + + // toPrecision returns exponential notation if the number of significant digits + // specified is less than the number of digits necessary to represent the integer + // part of the value in fixed-point notation. + + // Exponential notation. + if (id == 1 || id == 2 && (i <= e || e <= TO_EXP_NEG)) { + + // Append zeros? + for (; len < i; str += '0', len++); + str = toExponential(str, e); + + // Fixed-point notation. + } else { + i -= ne; + str = toFixedPoint(str, e, '0'); + + // Append zeros? + if (e + 1 > len) { + if (--i > 0) for (str += '.'; i--; str += '0'); + } else { + i += e - len; + if (i > 0) { + if (e + 1 == len) str += '.'; + for (; i--; str += '0'); + } + } + } + } + + return n.s < 0 && c0 ? '-' + str : str; + } + + + // Handle BigNumber.max and BigNumber.min. + function maxOrMin(args, method) { + var n, + i = 1, + m = new BigNumber(args[0]); + + for (; i < args.length; i++) { + n = new BigNumber(args[i]); + + // If any number is NaN, return NaN. + if (!n.s) { + m = n; + break; + } else if (method.call(m, n)) { + m = n; + } + } + + return m; + } + + + /* + * Strip trailing zeros, calculate base 10 exponent and check against MIN_EXP and MAX_EXP. + * Called by minus, plus and times. + */ + function normalise(n, c, e) { + var i = 1, + j = c.length; + + // Remove trailing zeros. + for (; !c[--j]; c.pop()); + + // Calculate the base 10 exponent. First get the number of digits of c[0]. + for (j = c[0]; j >= 10; j /= 10, i++); + + // Overflow? + if ((e = i + e * LOG_BASE - 1) > MAX_EXP) { + + // Infinity. + n.c = n.e = null; + + // Underflow? + } else if (e < MIN_EXP) { + + // Zero. + n.c = [n.e = 0]; + } else { + n.e = e; + n.c = c; + } + + return n; + } + + + // Handle values that fail the validity test in BigNumber. + parseNumeric = (function () { + var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i, + dotAfter = /^([^.]+)\.$/, + dotBefore = /^\.([^.]+)$/, + isInfinityOrNaN = /^-?(Infinity|NaN)$/, + whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g; + + return function (x, str, isNum, b) { + var base, + s = isNum ? str : str.replace(whitespaceOrPlus, ''); + + // No exception on ±Infinity or NaN. + if (isInfinityOrNaN.test(s)) { + x.s = isNaN(s) ? null : s < 0 ? -1 : 1; + } else { + if (!isNum) { + + // basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i + s = s.replace(basePrefix, function (m, p1, p2) { + base = (p2 = p2.toLowerCase()) == 'x' ? 16 : p2 == 'b' ? 2 : 8; + return !b || b == base ? p1 : m; + }); + + if (b) { + base = b; + + // E.g. '1.' to '1', '.1' to '0.1' + s = s.replace(dotAfter, '$1').replace(dotBefore, '0.$1'); + } + + if (str != s) return new BigNumber(s, base); + } + + // '[BigNumber Error] Not a number: {n}' + // '[BigNumber Error] Not a base {b} number: {n}' + if (BigNumber.DEBUG) { + throw Error + (bignumberError + 'Not a' + (b ? ' base ' + b : '') + ' number: ' + str); + } + + // NaN + x.s = null; + } + + x.c = x.e = null; + } + })(); + + + /* + * Round x to sd significant digits using rounding mode rm. Check for over/under-flow. + * If r is truthy, it is known that there are more digits after the rounding digit. + */ + function round(x, sd, rm, r) { + var d, i, j, k, n, ni, rd, + xc = x.c, + pows10 = POWS_TEN; + + // if x is not Infinity or NaN... + if (xc) { + + // rd is the rounding digit, i.e. the digit after the digit that may be rounded up. + // n is a base 1e14 number, the value of the element of array x.c containing rd. + // ni is the index of n within x.c. + // d is the number of digits of n. + // i is the index of rd within n including leading zeros. + // j is the actual index of rd within n (if < 0, rd is a leading zero). + out: { + + // Get the number of digits of the first element of xc. + for (d = 1, k = xc[0]; k >= 10; k /= 10, d++); + i = sd - d; + + // If the rounding digit is in the first element of xc... + if (i < 0) { + i += LOG_BASE; + j = sd; + n = xc[ni = 0]; + + // Get the rounding digit at index j of n. + rd = n / pows10[d - j - 1] % 10 | 0; + } else { + ni = mathceil((i + 1) / LOG_BASE); + + if (ni >= xc.length) { + + if (r) { + + // Needed by sqrt. + for (; xc.length <= ni; xc.push(0)); + n = rd = 0; + d = 1; + i %= LOG_BASE; + j = i - LOG_BASE + 1; + } else { + break out; + } + } else { + n = k = xc[ni]; + + // Get the number of digits of n. + for (d = 1; k >= 10; k /= 10, d++); + + // Get the index of rd within n. + i %= LOG_BASE; + + // Get the index of rd within n, adjusted for leading zeros. + // The number of leading zeros of n is given by LOG_BASE - d. + j = i - LOG_BASE + d; + + // Get the rounding digit at index j of n. + rd = j < 0 ? 0 : n / pows10[d - j - 1] % 10 | 0; + } + } + + r = r || sd < 0 || + + // Are there any non-zero digits after the rounding digit? + // The expression n % pows10[d - j - 1] returns all digits of n to the right + // of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714. + xc[ni + 1] != null || (j < 0 ? n : n % pows10[d - j - 1]); + + r = rm < 4 + ? (rd || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) + : rd > 5 || rd == 5 && (rm == 4 || r || rm == 6 && + + // Check whether the digit to the left of the rounding digit is odd. + ((i > 0 ? j > 0 ? n / pows10[d - j] : 0 : xc[ni - 1]) % 10) & 1 || + rm == (x.s < 0 ? 8 : 7)); + + if (sd < 1 || !xc[0]) { + xc.length = 0; + + if (r) { + + // Convert sd to decimal places. + sd -= x.e + 1; + + // 1, 0.1, 0.01, 0.001, 0.0001 etc. + xc[0] = pows10[(LOG_BASE - sd % LOG_BASE) % LOG_BASE]; + x.e = -sd || 0; + } else { + + // Zero. + xc[0] = x.e = 0; + } + + return x; + } + + // Remove excess digits. + if (i == 0) { + xc.length = ni; + k = 1; + ni--; + } else { + xc.length = ni + 1; + k = pows10[LOG_BASE - i]; + + // E.g. 56700 becomes 56000 if 7 is the rounding digit. + // j > 0 means i > number of leading zeros of n. + xc[ni] = j > 0 ? mathfloor(n / pows10[d - j] % pows10[j]) * k : 0; + } + + // Round up? + if (r) { + + for (; ;) { + + // If the digit to be rounded up is in the first element of xc... + if (ni == 0) { + + // i will be the length of xc[0] before k is added. + for (i = 1, j = xc[0]; j >= 10; j /= 10, i++); + j = xc[0] += k; + for (k = 1; j >= 10; j /= 10, k++); + + // if i != k the length has increased. + if (i != k) { + x.e++; + if (xc[0] == BASE) xc[0] = 1; + } + + break; + } else { + xc[ni] += k; + if (xc[ni] != BASE) break; + xc[ni--] = 0; + k = 1; + } + } + } + + // Remove trailing zeros. + for (i = xc.length; xc[--i] === 0; xc.pop()); + } + + // Overflow? Infinity. + if (x.e > MAX_EXP) { + x.c = x.e = null; + + // Underflow? Zero. + } else if (x.e < MIN_EXP) { + x.c = [x.e = 0]; + } + } + + return x; + } + + + function valueOf(n) { + var str, + e = n.e; + + if (e === null) return n.toString(); + + str = coeffToString(n.c); + + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential(str, e) + : toFixedPoint(str, e, '0'); + + return n.s < 0 ? '-' + str : str; + } + + + // PROTOTYPE/INSTANCE METHODS + + + /* + * Return a new BigNumber whose value is the absolute value of this BigNumber. + */ + P.absoluteValue = P.abs = function () { + var x = new BigNumber(this); + if (x.s < 0) x.s = 1; + return x; + }; + + + /* + * Return + * 1 if the value of this BigNumber is greater than the value of BigNumber(y, b), + * -1 if the value of this BigNumber is less than the value of BigNumber(y, b), + * 0 if they have the same value, + * or null if the value of either is NaN. + */ + P.comparedTo = function (y, b) { + return compare(this, new BigNumber(y, b)); + }; + + + /* + * If dp is undefined or null or true or false, return the number of decimal places of the + * value of this BigNumber, or null if the value of this BigNumber is ±Infinity or NaN. + * + * Otherwise, if dp is a number, return a new BigNumber whose value is the value of this + * BigNumber rounded to a maximum of dp decimal places using rounding mode rm, or + * ROUNDING_MODE if rm is omitted. + * + * [dp] {number} Decimal places: integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}' + */ + P.decimalPlaces = P.dp = function (dp, rm) { + var c, n, v, + x = this; + + if (dp != null) { + intCheck(dp, 0, MAX); + if (rm == null) rm = ROUNDING_MODE; + else intCheck(rm, 0, 8); + + return round(new BigNumber(x), dp + x.e + 1, rm); + } + + if (!(c = x.c)) return null; + n = ((v = c.length - 1) - bitFloor(this.e / LOG_BASE)) * LOG_BASE; + + // Subtract the number of trailing zeros of the last number. + if (v = c[v]) for (; v % 10 == 0; v /= 10, n--); + if (n < 0) n = 0; + + return n; + }; + + + /* + * n / 0 = I + * n / N = N + * n / I = 0 + * 0 / n = 0 + * 0 / 0 = N + * 0 / N = N + * 0 / I = 0 + * N / n = N + * N / 0 = N + * N / N = N + * N / I = N + * I / n = I + * I / 0 = I + * I / N = N + * I / I = N + * + * Return a new BigNumber whose value is the value of this BigNumber divided by the value of + * BigNumber(y, b), rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.dividedBy = P.div = function (y, b) { + return div(this, new BigNumber(y, b), DECIMAL_PLACES, ROUNDING_MODE); + }; + + + /* + * Return a new BigNumber whose value is the integer part of dividing the value of this + * BigNumber by the value of BigNumber(y, b). + */ + P.dividedToIntegerBy = P.idiv = function (y, b) { + return div(this, new BigNumber(y, b), 0, 1); + }; + + + /* + * Return a BigNumber whose value is the value of this BigNumber exponentiated by n. + * + * If m is present, return the result modulo m. + * If n is negative round according to DECIMAL_PLACES and ROUNDING_MODE. + * If POW_PRECISION is non-zero and m is not present, round to POW_PRECISION using ROUNDING_MODE. + * + * The modular power operation works efficiently when x, n, and m are integers, otherwise it + * is equivalent to calculating x.exponentiatedBy(n).modulo(m) with a POW_PRECISION of 0. + * + * n {number|string|BigNumber} The exponent. An integer. + * [m] {number|string|BigNumber} The modulus. + * + * '[BigNumber Error] Exponent not an integer: {n}' + */ + P.exponentiatedBy = P.pow = function (n, m) { + var half, isModExp, i, k, more, nIsBig, nIsNeg, nIsOdd, y, + x = this; + + n = new BigNumber(n); + + // Allow NaN and ±Infinity, but not other non-integers. + if (n.c && !n.isInteger()) { + throw Error + (bignumberError + 'Exponent not an integer: ' + valueOf(n)); + } + + if (m != null) m = new BigNumber(m); + + // Exponent of MAX_SAFE_INTEGER is 15. + nIsBig = n.e > 14; + + // If x is NaN, ±Infinity, ±0 or ±1, or n is ±Infinity, NaN or ±0. + if (!x.c || !x.c[0] || x.c[0] == 1 && !x.e && x.c.length == 1 || !n.c || !n.c[0]) { + + // The sign of the result of pow when x is negative depends on the evenness of n. + // If +n overflows to ±Infinity, the evenness of n would be not be known. + y = new BigNumber(Math.pow(+valueOf(x), nIsBig ? n.s * (2 - isOdd(n)) : +valueOf(n))); + return m ? y.mod(m) : y; + } + + nIsNeg = n.s < 0; + + if (m) { + + // x % m returns NaN if abs(m) is zero, or m is NaN. + if (m.c ? !m.c[0] : !m.s) return new BigNumber(NaN); + + isModExp = !nIsNeg && x.isInteger() && m.isInteger(); + + if (isModExp) x = x.mod(m); + + // Overflow to ±Infinity: >=2**1e10 or >=1.0000024**1e15. + // Underflow to ±0: <=0.79**1e10 or <=0.9999975**1e15. + } else if (n.e > 9 && (x.e > 0 || x.e < -1 || (x.e == 0 + // [1, 240000000] + ? x.c[0] > 1 || nIsBig && x.c[1] >= 24e7 + // [80000000000000] [99999750000000] + : x.c[0] < 8e13 || nIsBig && x.c[0] <= 9999975e7))) { + + // If x is negative and n is odd, k = -0, else k = 0. + k = x.s < 0 && isOdd(n) ? -0 : 0; + + // If x >= 1, k = ±Infinity. + if (x.e > -1) k = 1 / k; + + // If n is negative return ±0, else return ±Infinity. + return new BigNumber(nIsNeg ? 1 / k : k); + + } else if (POW_PRECISION) { + + // Truncating each coefficient array to a length of k after each multiplication + // equates to truncating significant digits to POW_PRECISION + [28, 41], + // i.e. there will be a minimum of 28 guard digits retained. + k = mathceil(POW_PRECISION / LOG_BASE + 2); + } + + if (nIsBig) { + half = new BigNumber(0.5); + if (nIsNeg) n.s = 1; + nIsOdd = isOdd(n); + } else { + i = Math.abs(+valueOf(n)); + nIsOdd = i % 2; + } + + y = new BigNumber(ONE); + + // Performs 54 loop iterations for n of 9007199254740991. + for (; ;) { + + if (nIsOdd) { + y = y.times(x); + if (!y.c) break; + + if (k) { + if (y.c.length > k) y.c.length = k; + } else if (isModExp) { + y = y.mod(m); //y = y.minus(div(y, m, 0, MODULO_MODE).times(m)); + } + } + + if (i) { + i = mathfloor(i / 2); + if (i === 0) break; + nIsOdd = i % 2; + } else { + n = n.times(half); + round(n, n.e + 1, 1); + + if (n.e > 14) { + nIsOdd = isOdd(n); + } else { + i = +valueOf(n); + if (i === 0) break; + nIsOdd = i % 2; + } + } + + x = x.times(x); + + if (k) { + if (x.c && x.c.length > k) x.c.length = k; + } else if (isModExp) { + x = x.mod(m); //x = x.minus(div(x, m, 0, MODULO_MODE).times(m)); + } + } + + if (isModExp) return y; + if (nIsNeg) y = ONE.div(y); + + return m ? y.mod(m) : k ? round(y, POW_PRECISION, ROUNDING_MODE, more) : y; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber rounded to an integer + * using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {rm}' + */ + P.integerValue = function (rm) { + var n = new BigNumber(this); + if (rm == null) rm = ROUNDING_MODE; + else intCheck(rm, 0, 8); + return round(n, n.e + 1, rm); + }; + + + /* + * Return true if the value of this BigNumber is equal to the value of BigNumber(y, b), + * otherwise return false. + */ + P.isEqualTo = P.eq = function (y, b) { + return compare(this, new BigNumber(y, b)) === 0; + }; + + + /* + * Return true if the value of this BigNumber is a finite number, otherwise return false. + */ + P.isFinite = function () { + return !!this.c; + }; + + + /* + * Return true if the value of this BigNumber is greater than the value of BigNumber(y, b), + * otherwise return false. + */ + P.isGreaterThan = P.gt = function (y, b) { + return compare(this, new BigNumber(y, b)) > 0; + }; + + + /* + * Return true if the value of this BigNumber is greater than or equal to the value of + * BigNumber(y, b), otherwise return false. + */ + P.isGreaterThanOrEqualTo = P.gte = function (y, b) { + return (b = compare(this, new BigNumber(y, b))) === 1 || b === 0; + + }; + + + /* + * Return true if the value of this BigNumber is an integer, otherwise return false. + */ + P.isInteger = function () { + return !!this.c && bitFloor(this.e / LOG_BASE) > this.c.length - 2; + }; + + + /* + * Return true if the value of this BigNumber is less than the value of BigNumber(y, b), + * otherwise return false. + */ + P.isLessThan = P.lt = function (y, b) { + return compare(this, new BigNumber(y, b)) < 0; + }; + + + /* + * Return true if the value of this BigNumber is less than or equal to the value of + * BigNumber(y, b), otherwise return false. + */ + P.isLessThanOrEqualTo = P.lte = function (y, b) { + return (b = compare(this, new BigNumber(y, b))) === -1 || b === 0; + }; + + + /* + * Return true if the value of this BigNumber is NaN, otherwise return false. + */ + P.isNaN = function () { + return !this.s; + }; + + + /* + * Return true if the value of this BigNumber is negative, otherwise return false. + */ + P.isNegative = function () { + return this.s < 0; + }; + + + /* + * Return true if the value of this BigNumber is positive, otherwise return false. + */ + P.isPositive = function () { + return this.s > 0; + }; + + + /* + * Return true if the value of this BigNumber is 0 or -0, otherwise return false. + */ + P.isZero = function () { + return !!this.c && this.c[0] == 0; + }; + + + /* + * n - 0 = n + * n - N = N + * n - I = -I + * 0 - n = -n + * 0 - 0 = 0 + * 0 - N = N + * 0 - I = -I + * N - n = N + * N - 0 = N + * N - N = N + * N - I = N + * I - n = I + * I - 0 = I + * I - N = N + * I - I = N + * + * Return a new BigNumber whose value is the value of this BigNumber minus the value of + * BigNumber(y, b). + */ + P.minus = function (y, b) { + var i, j, t, xLTy, + x = this, + a = x.s; + + y = new BigNumber(y, b); + b = y.s; + + // Either NaN? + if (!a || !b) return new BigNumber(NaN); + + // Signs differ? + if (a != b) { + y.s = -b; + return x.plus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if (!xe || !ye) { + + // Either Infinity? + if (!xc || !yc) return xc ? (y.s = -b, y) : new BigNumber(yc ? x : NaN); + + // Either zero? + if (!xc[0] || !yc[0]) { + + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + return yc[0] ? (y.s = -b, y) : new BigNumber(xc[0] ? x : + + // IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity + ROUNDING_MODE == 3 ? -0 : 0); + } + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Determine which is the bigger number. + if (a = xe - ye) { + + if (xLTy = a < 0) { + a = -a; + t = xc; + } else { + ye = xe; + t = yc; + } + + t.reverse(); + + // Prepend zeros to equalise exponents. + for (b = a; b--; t.push(0)); + t.reverse(); + } else { + + // Exponents equal. Check digit by digit. + j = (xLTy = (a = xc.length) < (b = yc.length)) ? a : b; + + for (a = b = 0; b < j; b++) { + + if (xc[b] != yc[b]) { + xLTy = xc[b] < yc[b]; + break; + } + } + } + + // x < y? Point xc to the array of the bigger number. + if (xLTy) { + t = xc; + xc = yc; + yc = t; + y.s = -y.s; + } + + b = (j = yc.length) - (i = xc.length); + + // Append zeros to xc if shorter. + // No need to add zeros to yc if shorter as subtract only needs to start at yc.length. + if (b > 0) for (; b--; xc[i++] = 0); + b = BASE - 1; + + // Subtract yc from xc. + for (; j > a;) { + + if (xc[--j] < yc[j]) { + for (i = j; i && !xc[--i]; xc[i] = b); + --xc[i]; + xc[j] += BASE; + } + + xc[j] -= yc[j]; + } + + // Remove leading zeros and adjust exponent accordingly. + for (; xc[0] == 0; xc.splice(0, 1), --ye); + + // Zero? + if (!xc[0]) { + + // Following IEEE 754 (2008) 6.3, + // n - n = +0 but n - n = -0 when rounding towards -Infinity. + y.s = ROUNDING_MODE == 3 ? -1 : 1; + y.c = [y.e = 0]; + return y; + } + + // No need to check for Infinity as +x - +y != Infinity && -x - -y != Infinity + // for finite x and y. + return normalise(y, xc, ye); + }; + + + /* + * n % 0 = N + * n % N = N + * n % I = n + * 0 % n = 0 + * -0 % n = -0 + * 0 % 0 = N + * 0 % N = N + * 0 % I = 0 + * N % n = N + * N % 0 = N + * N % N = N + * N % I = N + * I % n = N + * I % 0 = N + * I % N = N + * I % I = N + * + * Return a new BigNumber whose value is the value of this BigNumber modulo the value of + * BigNumber(y, b). The result depends on the value of MODULO_MODE. + */ + P.modulo = P.mod = function (y, b) { + var q, s, + x = this; + + y = new BigNumber(y, b); + + // Return NaN if x is Infinity or NaN, or y is NaN or zero. + if (!x.c || !y.s || y.c && !y.c[0]) { + return new BigNumber(NaN); + + // Return x if y is Infinity or x is zero. + } else if (!y.c || x.c && !x.c[0]) { + return new BigNumber(x); + } + + if (MODULO_MODE == 9) { + + // Euclidian division: q = sign(y) * floor(x / abs(y)) + // r = x - qy where 0 <= r < abs(y) + s = y.s; + y.s = 1; + q = div(x, y, 0, 3); + y.s = s; + q.s *= s; + } else { + q = div(x, y, 0, MODULO_MODE); + } + + y = x.minus(q.times(y)); + + // To match JavaScript %, ensure sign of zero is sign of dividend. + if (!y.c[0] && MODULO_MODE == 1) y.s = x.s; + + return y; + }; + + + /* + * n * 0 = 0 + * n * N = N + * n * I = I + * 0 * n = 0 + * 0 * 0 = 0 + * 0 * N = N + * 0 * I = N + * N * n = N + * N * 0 = N + * N * N = N + * N * I = N + * I * n = I + * I * 0 = N + * I * N = N + * I * I = I + * + * Return a new BigNumber whose value is the value of this BigNumber multiplied by the value + * of BigNumber(y, b). + */ + P.multipliedBy = P.times = function (y, b) { + var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, + base, sqrtBase, + x = this, + xc = x.c, + yc = (y = new BigNumber(y, b)).c; + + // Either NaN, ±Infinity or ±0? + if (!xc || !yc || !xc[0] || !yc[0]) { + + // Return NaN if either is NaN, or one is 0 and the other is Infinity. + if (!x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc) { + y.c = y.e = y.s = null; + } else { + y.s *= x.s; + + // Return ±Infinity if either is ±Infinity. + if (!xc || !yc) { + y.c = y.e = null; + + // Return ±0 if either is ±0. + } else { + y.c = [0]; + y.e = 0; + } + } + + return y; + } + + e = bitFloor(x.e / LOG_BASE) + bitFloor(y.e / LOG_BASE); + y.s *= x.s; + xcL = xc.length; + ycL = yc.length; + + // Ensure xc points to longer array and xcL to its length. + if (xcL < ycL) { + zc = xc; + xc = yc; + yc = zc; + i = xcL; + xcL = ycL; + ycL = i; + } + + // Initialise the result array with zeros. + for (i = xcL + ycL, zc = []; i--; zc.push(0)); + + base = BASE; + sqrtBase = SQRT_BASE; + + for (i = ycL; --i >= 0;) { + c = 0; + ylo = yc[i] % sqrtBase; + yhi = yc[i] / sqrtBase | 0; + + for (k = xcL, j = i + k; j > i;) { + xlo = xc[--k] % sqrtBase; + xhi = xc[k] / sqrtBase | 0; + m = yhi * xlo + xhi * ylo; + xlo = ylo * xlo + ((m % sqrtBase) * sqrtBase) + zc[j] + c; + c = (xlo / base | 0) + (m / sqrtBase | 0) + yhi * xhi; + zc[j--] = xlo % base; + } + + zc[j] = c; + } + + if (c) { + ++e; + } else { + zc.splice(0, 1); + } + + return normalise(y, zc, e); + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber negated, + * i.e. multiplied by -1. + */ + P.negated = function () { + var x = new BigNumber(this); + x.s = -x.s || null; + return x; + }; + + + /* + * n + 0 = n + * n + N = N + * n + I = I + * 0 + n = n + * 0 + 0 = 0 + * 0 + N = N + * 0 + I = I + * N + n = N + * N + 0 = N + * N + N = N + * N + I = N + * I + n = I + * I + 0 = I + * I + N = N + * I + I = I + * + * Return a new BigNumber whose value is the value of this BigNumber plus the value of + * BigNumber(y, b). + */ + P.plus = function (y, b) { + var t, + x = this, + a = x.s; + + y = new BigNumber(y, b); + b = y.s; + + // Either NaN? + if (!a || !b) return new BigNumber(NaN); + + // Signs differ? + if (a != b) { + y.s = -b; + return x.minus(y); + } + + var xe = x.e / LOG_BASE, + ye = y.e / LOG_BASE, + xc = x.c, + yc = y.c; + + if (!xe || !ye) { + + // Return ±Infinity if either ±Infinity. + if (!xc || !yc) return new BigNumber(a / 0); + + // Either zero? + // Return y if y is non-zero, x if x is non-zero, or zero if both are zero. + if (!xc[0] || !yc[0]) return yc[0] ? y : new BigNumber(xc[0] ? x : a * 0); + } + + xe = bitFloor(xe); + ye = bitFloor(ye); + xc = xc.slice(); + + // Prepend zeros to equalise exponents. Faster to use reverse then do unshifts. + if (a = xe - ye) { + if (a > 0) { + ye = xe; + t = yc; + } else { + a = -a; + t = xc; + } + + t.reverse(); + for (; a--; t.push(0)); + t.reverse(); + } + + a = xc.length; + b = yc.length; + + // Point xc to the longer array, and b to the shorter length. + if (a - b < 0) { + t = yc; + yc = xc; + xc = t; + b = a; + } + + // Only start adding at yc.length - 1 as the further digits of xc can be ignored. + for (a = 0; b;) { + a = (xc[--b] = xc[b] + yc[b] + a) / BASE | 0; + xc[b] = BASE === xc[b] ? 0 : xc[b] % BASE; + } + + if (a) { + xc = [a].concat(xc); + ++ye; + } + + // No need to check for zero, as +x + +y != 0 && -x + -y != 0 + // ye = MAX_EXP + 1 possible + return normalise(y, xc, ye); + }; + + + /* + * If sd is undefined or null or true or false, return the number of significant digits of + * the value of this BigNumber, or null if the value of this BigNumber is ±Infinity or NaN. + * If sd is true include integer-part trailing zeros in the count. + * + * Otherwise, if sd is a number, return a new BigNumber whose value is the value of this + * BigNumber rounded to a maximum of sd significant digits using rounding mode rm, or + * ROUNDING_MODE if rm is omitted. + * + * sd {number|boolean} number: significant digits: integer, 1 to MAX inclusive. + * boolean: whether to count integer-part trailing zeros: true or false. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {sd|rm}' + */ + P.precision = P.sd = function (sd, rm) { + var c, n, v, + x = this; + + if (sd != null && sd !== !!sd) { + intCheck(sd, 1, MAX); + if (rm == null) rm = ROUNDING_MODE; + else intCheck(rm, 0, 8); + + return round(new BigNumber(x), sd, rm); + } + + if (!(c = x.c)) return null; + v = c.length - 1; + n = v * LOG_BASE + 1; + + if (v = c[v]) { + + // Subtract the number of trailing zeros of the last element. + for (; v % 10 == 0; v /= 10, n--); + + // Add the number of digits of the first element. + for (v = c[0]; v >= 10; v /= 10, n++); + } + + if (sd && x.e + 1 > n) n = x.e + 1; + + return n; + }; + + + /* + * Return a new BigNumber whose value is the value of this BigNumber shifted by k places + * (powers of 10). Shift to the right if n > 0, and to the left if n < 0. + * + * k {number} Integer, -MAX_SAFE_INTEGER to MAX_SAFE_INTEGER inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {k}' + */ + P.shiftedBy = function (k) { + intCheck(k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER); + return this.times('1e' + k); + }; + + + /* + * sqrt(-n) = N + * sqrt(N) = N + * sqrt(-I) = N + * sqrt(I) = I + * sqrt(0) = 0 + * sqrt(-0) = -0 + * + * Return a new BigNumber whose value is the square root of the value of this BigNumber, + * rounded according to DECIMAL_PLACES and ROUNDING_MODE. + */ + P.squareRoot = P.sqrt = function () { + var m, n, r, rep, t, + x = this, + c = x.c, + s = x.s, + e = x.e, + dp = DECIMAL_PLACES + 4, + half = new BigNumber('0.5'); + + // Negative/NaN/Infinity/zero? + if (s !== 1 || !c || !c[0]) { + return new BigNumber(!s || s < 0 && (!c || c[0]) ? NaN : c ? x : 1 / 0); + } + + // Initial estimate. + s = Math.sqrt(+valueOf(x)); + + // Math.sqrt underflow/overflow? + // Pass x to Math.sqrt as integer, then adjust the exponent of the result. + if (s == 0 || s == 1 / 0) { + n = coeffToString(c); + if ((n.length + e) % 2 == 0) n += '0'; + s = Math.sqrt(+n); + e = bitFloor((e + 1) / 2) - (e < 0 || e % 2); + + if (s == 1 / 0) { + n = '5e' + e; + } else { + n = s.toExponential(); + n = n.slice(0, n.indexOf('e') + 1) + e; + } + + r = new BigNumber(n); + } else { + r = new BigNumber(s + ''); + } + + // Check for zero. + // r could be zero if MIN_EXP is changed after the this value was created. + // This would cause a division by zero (x/t) and hence Infinity below, which would cause + // coeffToString to throw. + if (r.c[0]) { + e = r.e; + s = e + dp; + if (s < 3) s = 0; + + // Newton-Raphson iteration. + for (; ;) { + t = r; + r = half.times(t.plus(div(x, t, dp, 1))); + + if (coeffToString(t.c).slice(0, s) === (n = coeffToString(r.c)).slice(0, s)) { + + // The exponent of r may here be one less than the final result exponent, + // e.g 0.0009999 (e-4) --> 0.001 (e-3), so adjust s so the rounding digits + // are indexed correctly. + if (r.e < e) --s; + n = n.slice(s - 3, s + 1); + + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits + // are 9999 or 4999 (i.e. approaching a rounding boundary) continue the + // iteration. + if (n == '9999' || !rep && n == '4999') { + + // On the first iteration only, check to see if rounding up gives the + // exact result as the nines may infinitely repeat. + if (!rep) { + round(t, t.e + DECIMAL_PLACES + 2, 0); + + if (t.times(t).eq(x)) { + r = t; + break; + } + } + + dp += 4; + s += 4; + rep = 1; + } else { + + // If rounding digits are null, 0{0,4} or 50{0,3}, check for exact + // result. If not, then there are further digits and m will be truthy. + if (!+n || !+n.slice(1) && n.charAt(0) == '5') { + + // Truncate to the first rounding digit. + round(r, r.e + DECIMAL_PLACES + 2, 1); + m = !r.times(r).eq(x); + } + + break; + } + } + } + } + + return round(r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m); + }; + + + /* + * Return a string representing the value of this BigNumber in exponential notation and + * rounded using ROUNDING_MODE to dp fixed decimal places. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}' + */ + P.toExponential = function (dp, rm) { + if (dp != null) { + intCheck(dp, 0, MAX); + dp++; + } + return format(this, dp, rm, 1); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounding + * to dp fixed decimal places using rounding mode rm, or ROUNDING_MODE if rm is omitted. + * + * Note: as with JavaScript's number type, (-0).toFixed(0) is '0', + * but e.g. (-0.00001).toFixed(0) is '-0'. + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}' + */ + P.toFixed = function (dp, rm) { + if (dp != null) { + intCheck(dp, 0, MAX); + dp = dp + this.e + 1; + } + return format(this, dp, rm); + }; + + + /* + * Return a string representing the value of this BigNumber in fixed-point notation rounded + * using rm or ROUNDING_MODE to dp decimal places, and formatted according to the properties + * of the format or FORMAT object (see BigNumber.set). + * + * The formatting object may contain some or all of the properties shown below. + * + * FORMAT = { + * prefix: '', + * groupSize: 3, + * secondaryGroupSize: 0, + * groupSeparator: ',', + * decimalSeparator: '.', + * fractionGroupSize: 0, + * fractionGroupSeparator: '\xA0', // non-breaking space + * suffix: '' + * }; + * + * [dp] {number} Decimal places. Integer, 0 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * [format] {object} Formatting options. See FORMAT pbject above. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {dp|rm}' + * '[BigNumber Error] Argument not an object: {format}' + */ + P.toFormat = function (dp, rm, format) { + var str, + x = this; + + if (format == null) { + if (dp != null && rm && typeof rm == 'object') { + format = rm; + rm = null; + } else if (dp && typeof dp == 'object') { + format = dp; + dp = rm = null; + } else { + format = FORMAT; + } + } else if (typeof format != 'object') { + throw Error + (bignumberError + 'Argument not an object: ' + format); + } + + str = x.toFixed(dp, rm); + + if (x.c) { + var i, + arr = str.split('.'), + g1 = +format.groupSize, + g2 = +format.secondaryGroupSize, + groupSeparator = format.groupSeparator || '', + intPart = arr[0], + fractionPart = arr[1], + isNeg = x.s < 0, + intDigits = isNeg ? intPart.slice(1) : intPart, + len = intDigits.length; + + if (g2) { + i = g1; + g1 = g2; + g2 = i; + len -= i; + } + + if (g1 > 0 && len > 0) { + i = len % g1 || g1; + intPart = intDigits.substr(0, i); + for (; i < len; i += g1) intPart += groupSeparator + intDigits.substr(i, g1); + if (g2 > 0) intPart += groupSeparator + intDigits.slice(i); + if (isNeg) intPart = '-' + intPart; + } + + str = fractionPart + ? intPart + (format.decimalSeparator || '') + ((g2 = +format.fractionGroupSize) + ? fractionPart.replace(new RegExp('\\d{' + g2 + '}\\B', 'g'), + '$&' + (format.fractionGroupSeparator || '')) + : fractionPart) + : intPart; + } + + return (format.prefix || '') + str + (format.suffix || ''); + }; + + + /* + * Return an array of two BigNumbers representing the value of this BigNumber as a simple + * fraction with an integer numerator and an integer denominator. + * The denominator will be a positive non-zero value less than or equal to the specified + * maximum denominator. If a maximum denominator is not specified, the denominator will be + * the lowest value necessary to represent the number exactly. + * + * [md] {number|string|BigNumber} Integer >= 1, or Infinity. The maximum denominator. + * + * '[BigNumber Error] Argument {not an integer|out of range} : {md}' + */ + P.toFraction = function (md) { + var d, d0, d1, d2, e, exp, n, n0, n1, q, r, s, + x = this, + xc = x.c; + + if (md != null) { + n = new BigNumber(md); + + // Throw if md is less than one or is not an integer, unless it is Infinity. + if (!n.isInteger() && (n.c || n.s !== 1) || n.lt(ONE)) { + throw Error + (bignumberError + 'Argument ' + + (n.isInteger() ? 'out of range: ' : 'not an integer: ') + valueOf(n)); + } + } + + if (!xc) return new BigNumber(x); + + d = new BigNumber(ONE); + n1 = d0 = new BigNumber(ONE); + d1 = n0 = new BigNumber(ONE); + s = coeffToString(xc); + + // Determine initial denominator. + // d is a power of 10 and the minimum max denominator that specifies the value exactly. + e = d.e = s.length - x.e - 1; + d.c[0] = POWS_TEN[(exp = e % LOG_BASE) < 0 ? LOG_BASE + exp : exp]; + md = !md || n.comparedTo(d) > 0 ? (e > 0 ? d : n1) : n; + + exp = MAX_EXP; + MAX_EXP = 1 / 0; + n = new BigNumber(s); + + // n0 = d1 = 0 + n0.c[0] = 0; + + for (; ;) { + q = div(n, d, 0, 1); + d2 = d0.plus(q.times(d1)); + if (d2.comparedTo(md) == 1) break; + d0 = d1; + d1 = d2; + n1 = n0.plus(q.times(d2 = n1)); + n0 = d2; + d = n.minus(q.times(d2 = d)); + n = d2; + } + + d2 = div(md.minus(d0), d1, 0, 1); + n0 = n0.plus(d2.times(n1)); + d0 = d0.plus(d2.times(d1)); + n0.s = n1.s = x.s; + e = e * 2; + + // Determine which fraction is closer to x, n0/d0 or n1/d1 + r = div(n1, d1, e, ROUNDING_MODE).minus(x).abs().comparedTo( + div(n0, d0, e, ROUNDING_MODE).minus(x).abs()) < 1 ? [n1, d1] : [n0, d0]; + + MAX_EXP = exp; + + return r; + }; + + + /* + * Return the value of this BigNumber converted to a number primitive. + */ + P.toNumber = function () { + return +valueOf(this); + }; + + + /* + * Return a string representing the value of this BigNumber rounded to sd significant digits + * using rounding mode rm or ROUNDING_MODE. If sd is less than the number of digits + * necessary to represent the integer part of the value in fixed-point notation, then use + * exponential notation. + * + * [sd] {number} Significant digits. Integer, 1 to MAX inclusive. + * [rm] {number} Rounding mode. Integer, 0 to 8 inclusive. + * + * '[BigNumber Error] Argument {not a primitive number|not an integer|out of range}: {sd|rm}' + */ + P.toPrecision = function (sd, rm) { + if (sd != null) intCheck(sd, 1, MAX); + return format(this, sd, rm, 2); + }; + + + /* + * Return a string representing the value of this BigNumber in base b, or base 10 if b is + * omitted. If a base is specified, including base 10, round according to DECIMAL_PLACES and + * ROUNDING_MODE. If a base is not specified, and this BigNumber has a positive exponent + * that is equal to or greater than TO_EXP_POS, or a negative exponent equal to or less than + * TO_EXP_NEG, return exponential notation. + * + * [b] {number} Integer, 2 to ALPHABET.length inclusive. + * + * '[BigNumber Error] Base {not a primitive number|not an integer|out of range}: {b}' + */ + P.toString = function (b) { + var str, + n = this, + s = n.s, + e = n.e; + + // Infinity or NaN? + if (e === null) { + if (s) { + str = 'Infinity'; + if (s < 0) str = '-' + str; + } else { + str = 'NaN'; + } + } else { + if (b == null) { + str = e <= TO_EXP_NEG || e >= TO_EXP_POS + ? toExponential(coeffToString(n.c), e) + : toFixedPoint(coeffToString(n.c), e, '0'); + } else if (b === 10 && alphabetHasNormalDecimalDigits) { + n = round(new BigNumber(n), DECIMAL_PLACES + e + 1, ROUNDING_MODE); + str = toFixedPoint(coeffToString(n.c), n.e, '0'); + } else { + intCheck(b, 2, ALPHABET.length, 'Base'); + str = convertBase(toFixedPoint(coeffToString(n.c), e, '0'), 10, b, s, true); + } + + if (s < 0 && n.c[0]) str = '-' + str; + } + + return str; + }; + + + /* + * Return as toString, but do not accept a base argument, and include the minus sign for + * negative zero. + */ + P.valueOf = P.toJSON = function () { + return valueOf(this); + }; + + + P._isBigNumber = true; + + if (configObject != null) BigNumber.set(configObject); + + return BigNumber; + } + + + // PRIVATE HELPER FUNCTIONS + + // These functions don't need access to variables, + // e.g. DECIMAL_PLACES, in the scope of the `clone` function above. + + + function bitFloor(n) { + var i = n | 0; + return n > 0 || n === i ? i : i - 1; + } + + + // Return a coefficient array as a string of base 10 digits. + function coeffToString(a) { + var s, z, + i = 1, + j = a.length, + r = a[0] + ''; + + for (; i < j;) { + s = a[i++] + ''; + z = LOG_BASE - s.length; + for (; z--; s = '0' + s); + r += s; + } + + // Determine trailing zeros. + for (j = r.length; r.charCodeAt(--j) === 48;); + + return r.slice(0, j + 1 || 1); + } + + + // Compare the value of BigNumbers x and y. + function compare(x, y) { + var a, b, + xc = x.c, + yc = y.c, + i = x.s, + j = y.s, + k = x.e, + l = y.e; + + // Either NaN? + if (!i || !j) return null; + + a = xc && !xc[0]; + b = yc && !yc[0]; + + // Either zero? + if (a || b) return a ? b ? 0 : -j : i; + + // Signs differ? + if (i != j) return i; + + a = i < 0; + b = k == l; + + // Either Infinity? + if (!xc || !yc) return b ? 0 : !xc ^ a ? 1 : -1; + + // Compare exponents. + if (!b) return k > l ^ a ? 1 : -1; + + j = (k = xc.length) < (l = yc.length) ? k : l; + + // Compare digit by digit. + for (i = 0; i < j; i++) if (xc[i] != yc[i]) return xc[i] > yc[i] ^ a ? 1 : -1; + + // Compare lengths. + return k == l ? 0 : k > l ^ a ? 1 : -1; + } + + + /* + * Check that n is a primitive number, an integer, and in range, otherwise throw. + */ + function intCheck(n, min, max, name) { + if (n < min || n > max || n !== mathfloor(n)) { + throw Error + (bignumberError + (name || 'Argument') + (typeof n == 'number' + ? n < min || n > max ? ' out of range: ' : ' not an integer: ' + : ' not a primitive number: ') + String(n)); + } + } + + + // Assumes finite n. + function isOdd(n) { + var k = n.c.length - 1; + return bitFloor(n.e / LOG_BASE) == k && n.c[k] % 2 != 0; + } + + + function toExponential(str, e) { + return (str.length > 1 ? str.charAt(0) + '.' + str.slice(1) : str) + + (e < 0 ? 'e' : 'e+') + e; + } + + + function toFixedPoint(str, e, z) { + var len, zs; + + // Negative exponent? + if (e < 0) { + + // Prepend zeros. + for (zs = z + '.'; ++e; zs += z); + str = zs + str; + + // Positive exponent + } else { + len = str.length; + + // Append zeros. + if (++e > len) { + for (zs = z, e -= len; --e; zs += z); + str += zs; + } else if (e < len) { + str = str.slice(0, e) + '.' + str.slice(e); + } + } + + return str; + } + + + // EXPORT + + + BigNumber = clone(); + BigNumber['default'] = BigNumber.BigNumber = BigNumber; + + // AMD. + if (typeof define == 'function' && define.amd) { + define(function () { return BigNumber; }); + + // Node.js and other environments that support module.exports. + } else if (typeof module != 'undefined' && module.exports) { + module.exports = BigNumber; + + // Browser. + } else { + if (!globalObject) { + globalObject = typeof self != 'undefined' && self ? self : window; + } + + globalObject.BigNumber = BigNumber; + } +})(this); diff --git a/scripts/bit_reader.js b/scripts/bit_reader.js new file mode 100755 index 0000000..e7202b1 --- /dev/null +++ b/scripts/bit_reader.js @@ -0,0 +1,124 @@ +// Use strict +"use strict"; + + +// Classes + +// Bit reader class +class BitReader { + + // Public + + // Constructor + constructor(data) { + + // Set data + this.data = data; + + // Initialize byte index + this.byteIndex = 0; + + // Initialize bit index + this.bitIndex = 0; + } + + // Get bits + getBits(numberOfBits) { + + // Check if more than one byte is requested + if(numberOfBits > Common.BITS_IN_A_BYTE) { + + // Initialize result + var result = 0; + + // Go through all bytes + while(numberOfBits > 0) { + + // Update result to make space for more bits + result <<= Math.min(numberOfBits, Common.BITS_IN_A_BYTE); + + // Include bits in result + result |= this.getBits(Math.min(numberOfBits, Common.BITS_IN_A_BYTE)); + + // Update number of bits + numberOfBits -= Common.BITS_IN_A_BYTE; + } + + // Return result + return result; + } + + // Otherwise + else { + + // Check if no bits requested + if(numberOfBits === 0) { + + // Return zero + return 0; + } + + // Check if number of bits is invalid + if(this.byteIndex === this.data["length"] || (this.byteIndex === this.data["length"] - 1 && this.bitIndex + numberOfBits > Common.BITS_IN_A_BYTE)) { + + // Throw error + throw "Invalid number of bits."; + } + + // Initialize result to data at the byte index + var result = this.data[this.byteIndex] << Common.BITS_IN_A_BYTE; + + // Check if more data is needed + if(this.bitIndex + numberOfBits > Common.BITS_IN_A_BYTE) { + + // Append next byte to the result + result |= this.data[this.byteIndex + 1]; + } + + // Remove upper bits from result + result &= (1 << (Common.BITS_IN_A_BYTE * 2 - this.bitIndex)) - 1; + + // Remove lower bits from result + result >>>= (Common.BITS_IN_A_BYTE * 2 - (this.bitIndex + numberOfBits)); + + // Update bit index + this.bitIndex += numberOfBits; + + // Check if bit index overflowed into the next byte + if(this.bitIndex >= Common.BITS_IN_A_BYTE) { + + // Increment byte index + ++this.byteIndex; + + // Correct bit index + this.bitIndex %= Common.BITS_IN_A_BYTE; + } + + // Return result + return result; + } + } + + // Get bytes + getBytes(numberOfBytes) { + + // Initialize result + var result = new Uint8Array(numberOfBytes); + + // Go through all bytes + for(var i = 0; i < numberOfBytes; ++i) { + + // Set byte in the result + result[i] = this.getBits(Common.BITS_IN_A_BYTE); + } + + // Return result + return result; + } +} + + +// Main function + +// Set global object's bit reader +globalThis["BitReader"] = BitReader; diff --git a/scripts/bit_writer.js b/scripts/bit_writer.js new file mode 100755 index 0000000..818dc63 --- /dev/null +++ b/scripts/bit_writer.js @@ -0,0 +1,110 @@ +// Use strict +"use strict"; + + +// Classes + +// Bit writer class +class BitWriter { + + // Public + + // Constructor + constructor() { + + // Set data + this.data = new Uint8Array([]); + + // Initialize byte index + this.byteIndex = 0; + + // Initialize bit index + this.bitIndex = 0; + } + + // Set bits + setBits(value, numberOfBits) { + + // Go through all bytes past one byte + while(numberOfBits > Common.BITS_IN_A_BYTE) { + + // Set byte + this.setBits(value >>> (Common.BITS_IN_A_BYTE * (Math.floor(numberOfBits / Common.BITS_IN_A_BYTE) - 1) + numberOfBits % Common.BITS_IN_A_BYTE), Math.min(numberOfBits, Common.BITS_IN_A_BYTE)); + + // Update number of bits + numberOfBits -= Common.BITS_IN_A_BYTE; + } + + // Check if no bits requested + if(numberOfBits === 0) { + + // Return + return; + } + + // Check if more data is needed + if(this.bitIndex === 0 || this.bitIndex + numberOfBits > Common.BITS_IN_A_BYTE) { + + // Increase data's size by one + var temp = new Uint8Array(this.data["length"] + 1); + + temp.set(this.data); + + this.data = temp; + } + + // Check if value will overflow into the next byte + if(this.bitIndex + numberOfBits > Common.BITS_IN_A_BYTE) { + + // Include data in value at byte index + this.data[this.byteIndex] |= value >>> ((this.bitIndex + numberOfBits) - Common.BITS_IN_A_BYTE); + + // Include data in value at next byte index + this.data[this.byteIndex + 1] |= value << (Common.BITS_IN_A_BYTE * 2 - (this.bitIndex + numberOfBits)); + } + + // Otherwise + else { + + // Include data in value at byte index + this.data[this.byteIndex] |= value << (Common.BITS_IN_A_BYTE - (this.bitIndex + numberOfBits)); + } + + // Update bit index + this.bitIndex += numberOfBits; + + // Check if bit index overflowed into the next byte + if(this.bitIndex >= Common.BITS_IN_A_BYTE) { + + // Increment byte index + ++this.byteIndex; + + // Correct bit index + this.bitIndex %= Common.BITS_IN_A_BYTE; + } + } + + // Set bytes + setBytes(bytes) { + + // Go through all bytes + for(var i = 0; i < bytes["length"]; ++i) { + + // Set byte in the data + this.setBits(bytes[i], Common.BITS_IN_A_BYTE); + } + } + + // Get bytes + getBytes() { + + // Return data + return this.data; + } +} + + +// Main function + +// Set global object's bit writer +globalThis["BitWriter"] = BitWriter; diff --git a/scripts/camera.js b/scripts/camera.js new file mode 100755 index 0000000..abf57fa --- /dev/null +++ b/scripts/camera.js @@ -0,0 +1,434 @@ +// Use strict +"use strict"; + + +// Classes + +// Camera class +class Camera { + + // Public + + // Initialize + static initialize() { + + // Initialize request index + Camera.requestIndex = 0; + + // Create worker + Camera.worker = new Worker(Camera.WORKER_FILE_LOCATION); + + // Window before unload event + $(window).on("beforeunload", function() { + + // Get current request index + var currentRequestIndex = Camera.requestIndex++; + + // Check if current request index is at the max safe integer + if(currentRequestIndex === Number.MAX_SAFE_INTEGER) + + // Reset request index + Camera.requestIndex = 0; + + // Send worker an uninitialize request + Camera.worker.postMessage([ + + // Request index + currentRequestIndex, + + // Type + Camera.UNINITIALIZE_REQUEST_TYPE + ]); + + // Terminate worker + Camera.worker.terminate(); + }); + + // Return promise + return new Promise(function(resolve, reject) { + + // Worker on error + Camera.worker["onerror"] = function() { + + // Reject error + reject("Failed to create camera worker."); + }; + + // Worker on message + Camera.worker["onmessage"] = function(event) { + + // Get message + var message = event["data"]; + + // Get message's request index + var requestIndex = message[Camera.MESSAGE_REQUEST_INDEX_OFFSET]; + + // Check message's type + switch(message[Camera.MESSAGE_TYPE_OFFSET]) { + + // Initialize request type + case Camera.INITIALIZE_REQUEST_TYPE: + + // Get message's status + var status = message[Camera.MESSAGE_STATUS_OFFSET]; + + // Check if worker initialized successfully + if(status === Camera.STATUS_SUCCESS_VALUE) + + // Resolve + resolve(); + + // Otherwise + else + + // Reject error + reject("Failed to initialize camera worker."); + + // Break + break; + + // Default + default: + + // Get message's response + var response = message[Camera.MESSAGE_RESPONSE_OFFSET]; + + // Trigger response request index event + $(document).trigger(Camera.RESPONSE_EVENT + requestIndex.toFixed(), [ + + // Response + response + ]); + + // Break + break; + } + }; + + // Get current request index + var currentRequestIndex = Camera.requestIndex++; + + // Check if current request index is at the max safe integer + if(currentRequestIndex === Number.MAX_SAFE_INTEGER) + + // Reset request index + Camera.requestIndex = 0; + + // Send worker an initialize request + Camera.worker.postMessage([ + + // Request index + currentRequestIndex, + + // Type + Camera.INITIALIZE_REQUEST_TYPE, + + // URL query string + Common.URL_QUERY_STRING_SEPARATOR + encodeURIComponent(Consensus.OVERRIDE_WALLET_TYPE_URL_PARAMETER_NAME).replace(/%20/ug, "+") + Common.URL_QUERY_STRING_PARAMETER_VALUE_SEPARATOR + encodeURIComponent(Consensus.walletTypeToText(Consensus.getWalletType())).replace(/%20/ug, "+") + Common.URL_QUERY_STRING_PARAMETER_SEPARATOR + encodeURIComponent(Consensus.OVERRIDE_NETWORK_TYPE_URL_PARAMETER_NAME).replace(/%20/ug, "+") + Common.URL_QUERY_STRING_PARAMETER_VALUE_SEPARATOR + encodeURIComponent(Consensus.networkTypeToText(Consensus.getNetworkType())).replace(/%20/ug, "+") + ]); + }); + } + + // Get value + static getValue(image, width, height) { + + // Create canvas + var canvas = document.createElement("canvas"); + + // Set canvas's dimensions + canvas["width"] = width; + canvas["height"] = height; + + // Draw image on canvas + var context = canvas.getContext("2d"); + context.drawImage(image, 0, 0, width, height); + + // Get image's data from the canvas + var imageData = context.getImageData(0, 0, width, height)["data"]["buffer"]; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return sending decode request + return Camera.sendRequest([ + + // Type + Camera.DECODE_REQUEST_TYPE, + + // Width + width, + + // Height + height, + + // Image data + imageData + ], [ + + // Image data + imageData + + ]).then(function(response) { + + // Check if response is no value + if(response === Camera.NO_VALUE) + + // Resolve no value + resolve(Camera.NO_VALUE); + + // Otherwise + else { + + // Get data from response + var data = response[Camera.RESPONSE_DATA_INDEX]; + + // Get top left corner X from response + var topLeftCornerX = response[Camera.RESPONSE_TOP_LEFT_CORNER_X_INDEX]; + + // Get top left corner Y from response + var topLeftCornerY = response[Camera.RESPONSE_TOP_LEFT_CORNER_Y_INDEX]; + + // Get top right corner X from response + var topRightCornerX = response[Camera.RESPONSE_TOP_RIGHT_CORNER_X_INDEX]; + + // Get top right corner Y from response + var topRightCornerY = response[Camera.RESPONSE_TOP_RIGHT_CORNER_Y_INDEX]; + + // Get bottom left corner X from response + var bottomLeftCornerX = response[Camera.RESPONSE_BOTTOM_LEFT_CORNER_X_INDEX]; + + // Get bottom left corner Y from response + var bottomLeftCornerY = response[Camera.RESPONSE_BOTTOM_LEFT_CORNER_Y_INDEX]; + + // Get bottom right corner X from response + var bottomRightCornerX = response[Camera.RESPONSE_BOTTOM_RIGHT_CORNER_X_INDEX]; + + // Get bottom right corner Y from response + var bottomRightCornerY = response[Camera.RESPONSE_BOTTOM_RIGHT_CORNER_Y_INDEX]; + + // Resolve data + resolve(data); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Initialize request type + static get INITIALIZE_REQUEST_TYPE() { + + // Return initialize request type + return 0; + } + + // Uninitialize request type + static get UNINITIALIZE_REQUEST_TYPE() { + + // Return uninitialize request type + return Camera.INITIALIZE_REQUEST_TYPE + 1; + } + + // Decode request type + static get DECODE_REQUEST_TYPE() { + + // Return decode request type + return Camera.UNINITIALIZE_REQUEST_TYPE + 1; + } + + // Message request index offset + static get MESSAGE_REQUEST_INDEX_OFFSET() { + + // Return message request index offset + return 0; + } + + // Message type offset + static get MESSAGE_TYPE_OFFSET() { + + // Return message type offset + return Camera.MESSAGE_REQUEST_INDEX_OFFSET + 1; + } + + // Message initialize URL query string offset + static get MESSAGE_INITIALIZE_URL_QUERY_STRING_OFFSET() { + + // Return message initialize URL query string offset + return Camera.MESSAGE_TYPE_OFFSET + 1; + } + + // Message width offset + static get MESSAGE_WIDTH_OFFSET() { + + // Return message width offset + return Camera.MESSAGE_TYPE_OFFSET + 1; + } + + // Message height offset + static get MESSAGE_HEIGHT_OFFSET() { + + // Return message height offset + return Camera.MESSAGE_WIDTH_OFFSET + 1; + } + + // Message image data offset + static get MESSAGE_IMAGE_DATA_OFFSET() { + + // Return message image data offset + return Camera.MESSAGE_HEIGHT_OFFSET + 1; + } + + // Status success value + static get STATUS_SUCCESS_VALUE() { + + // Return status success value + return true; + } + + // Status failed value + static get STATUS_FAILED_VALUE() { + + // Return status failed value + return false; + } + + // No value + static get NO_VALUE() { + + // Return no value + return null; + } + + // Private + + // Send request + static sendRequest(request, transfer) { + + // Get current request index + var currentRequestIndex = Camera.requestIndex++; + + // Check if current request index is at the max safe integer + if(currentRequestIndex === Number.MAX_SAFE_INTEGER) + + // Reset request index + Camera.requestIndex = 0; + + // Add current request index to request + request.unshift(currentRequestIndex); + + // Return promise + return new Promise(function(resolve, reject) { + + // Response current request index event + $(document).one(Camera.RESPONSE_EVENT + currentRequestIndex.toFixed(), function(event, response) { + + // Resolve response + resolve(response); + }); + + // Send worker the request + Camera.worker.postMessage(request, transfer); + }); + } + + // Message status offset + static get MESSAGE_STATUS_OFFSET() { + + // Return message status offset + return Camera.MESSAGE_TYPE_OFFSET + 1; + } + + // Message response offset + static get MESSAGE_RESPONSE_OFFSET() { + + // Return message response offset + return Camera.MESSAGE_TYPE_OFFSET + 1; + } + + // Response event + static get RESPONSE_EVENT() { + + // Return response event + return "CameraResponseEvent"; + } + + // Response data index + static get RESPONSE_DATA_INDEX() { + + // Return response data index + return 0; + } + + // Response top left corner X index + static get RESPONSE_TOP_LEFT_CORNER_X_INDEX() { + + // Return response top left corner X index + return Camera.RESPONSE_DATA_INDEX + 1; + } + + // Response top left corner Y index + static get RESPONSE_TOP_LEFT_CORNER_Y_INDEX() { + + // Return response top left corner Y index + return Camera.RESPONSE_TOP_LEFT_CORNER_X_INDEX + 1; + } + + // Response top right corner X index + static get RESPONSE_TOP_RIGHT_CORNER_X_INDEX() { + + // Return response top right corner X index + return Camera.RESPONSE_TOP_LEFT_CORNER_Y_INDEX + 1; + } + + // Response top right corner Y index + static get RESPONSE_TOP_RIGHT_CORNER_Y_INDEX() { + + // Return response top right corner Y index + return Camera.RESPONSE_TOP_RIGHT_CORNER_X_INDEX + 1; + } + + // Response bottom left corner X index + static get RESPONSE_BOTTOM_LEFT_CORNER_X_INDEX() { + + // Return response bottom left corner X index + return Camera.RESPONSE_TOP_RIGHT_CORNER_Y_INDEX + 1; + } + + // Response bottom left corner Y index + static get RESPONSE_BOTTOM_LEFT_CORNER_Y_INDEX() { + + // Return response bottom left corner Y index + return Camera.RESPONSE_BOTTOM_LEFT_CORNER_X_INDEX + 1; + } + + // Response bottom right corner X index + static get RESPONSE_BOTTOM_RIGHT_CORNER_X_INDEX() { + + // Return response bottom right corner X index + return Camera.RESPONSE_BOTTOM_LEFT_CORNER_Y_INDEX + 1; + } + + // Response bottom right corner Y index + static get RESPONSE_BOTTOM_RIGHT_CORNER_Y_INDEX() { + + // Return response bottom right corner Y index + return Camera.RESPONSE_BOTTOM_RIGHT_CORNER_X_INDEX + 1; + } + + // Worker file location + static get WORKER_FILE_LOCATION() { + + // Return worker file location + return "." + getResource("./scripts/camera_worker.js"); + } +} + + +// Main function + +// Set global object's camera +globalThis["Camera"] = Camera; diff --git a/scripts/camera_worker.js b/scripts/camera_worker.js new file mode 100755 index 0000000..fd315da --- /dev/null +++ b/scripts/camera_worker.js @@ -0,0 +1,176 @@ +// Use strict +"use strict"; + + +// Import scripts +importScripts("."); +importScripts("."); + + +// Constants + +// No QR code +const NO_QR_CODE = null; + + +// Global variables + +// URL query string +var URL_QUERY_STRING; + + +// Events + +// Message event +self.addEventListener("message", function(event) { + + // Get message + var message = event["data"]; + + // Get message's request index + var requestIndex = message[Camera.MESSAGE_REQUEST_INDEX_OFFSET]; + + // Get message's type + var type = message[Camera.MESSAGE_TYPE_OFFSET]; + + // Check type + switch(type) { + + // Initialize request type + case Camera.INITIALIZE_REQUEST_TYPE: + + // Get message's URL query string + URL_QUERY_STRING = message[Camera.MESSAGE_INITIALIZE_URL_QUERY_STRING_OFFSET]; + + // Respond with success status + postMessage([ + + // Request index + requestIndex, + + // Type + type, + + // Status + Camera.STATUS_SUCCESS_VALUE + ]); + + // Break + break; + + // Uninitialize request type + case Camera.UNINITIALIZE_REQUEST_TYPE: + + // Respond with success status + postMessage([ + + // Request index + requestIndex, + + // Type + type, + + // Status + Camera.STATUS_SUCCESS_VALUE + ]); + + // Break + break; + + // Decode request type + case Camera.DECODE_REQUEST_TYPE: + + // Get message's width + var width = message[Camera.MESSAGE_WIDTH_OFFSET]; + + // Get message's height + var height = message[Camera.MESSAGE_HEIGHT_OFFSET]; + + // Get message's image data + var imageData = new Uint8Array(message[Camera.MESSAGE_IMAGE_DATA_OFFSET]); + + // Get QR code from image data, width, and height + var qrCode = jsQR(imageData, width, height, { + + // Inversion attempts + "inversionAttempts": "attemptBoth" + }); + + // Check if getting QR code failed + if(qrCode === NO_QR_CODE) { + + // Respond with no value + postMessage([ + + // Request index + requestIndex, + + // Type + type, + + // Value + Camera.NO_VALUE + ]); + } + + // Otherwise + else { + + // Respond with value + postMessage([ + + // Request index + requestIndex, + + // Type + type, + + // Value + [ + + // Data + qrCode["data"], + + // Top left corner X + qrCode["location"]["topLeftCorner"]["x"], + + // Top left corner Y + qrCode["location"]["topLeftCorner"]["y"], + + // Top right corner X + qrCode["location"]["topRightCorner"]["x"], + + // Top right corner Y + qrCode["location"]["topRightCorner"]["y"], + + // Bottom left corner X + qrCode["location"]["bottomLeftCorner"]["x"], + + // Bottom left corner Y + qrCode["location"]["bottomLeftCorner"]["y"], + + // Bottom right corner X + qrCode["location"]["bottomRightCorner"]["x"], + + // Bottom right corner Y + qrCode["location"]["bottomRightCorner"]["y"] + ] + ]); + } + + // Break; + break; + } +}); diff --git a/scripts/caps_lock.js b/scripts/caps_lock.js new file mode 100755 index 0000000..1c348f6 --- /dev/null +++ b/scripts/caps_lock.js @@ -0,0 +1,135 @@ +// Use strict +"use strict"; + + +// Classes + +// Caps lock class +class CapsLock { + + // Public + + // Constructor + constructor() { + + // Set state + this.state = false; + + // Get is linux + this.isLinux = (typeof navigator === "object" && navigator !== null) ? (("userAgentData" in navigator === true && "platform" in navigator["userAgentData"] === true) ? navigator["userAgentData"]["platform"] : navigator["platform"]).toLowerCase().indexOf("linux") !== Common.INDEX_NOT_FOUND : false; + + // Turning off + this.turningOff = false; + + // Get body display + this.bodyDisplay = $("body"); + + // Set self + var self = this; + + // Document key down, key up, mouse down, mouse up, and mousemove event + $(document).on("keydown keyup mousedown mouseup mousemove", function(event) { + + // Check if Linux, event is key up or key down, and caps lock is the key + if(self.isLinux === true && (event["type"] === "keyup" || event["type"] === "keydown") && event["originalEvent"]["code"] === "CapsLock") { + + // Check if event is key down + if(event["type"] === "keydown") { + + // Check if caps lock is off + if(event["originalEvent"].getModifierState("CapsLock") === false) { + + // Check if state is off + if(self.state === false) { + + // Set state + self.state = true; + + // Set that body display has caps lock + self.bodyDisplay.addClass("capsLock"); + } + } + + // Otherwise + else + + // Set turning off + self.turningOff = true; + } + + // Otherwise assume event is key up + else { + + // Check if turning off + if(self.turningOff === true) { + + // Clear turning off + self.turningOff = false; + + // Check if state is on + if(self.state === true) { + + // Clear state + self.state = false; + + // Set that body display doesn't have caps lock + self.bodyDisplay.removeClass("capsLock"); + } + } + } + } + + // Otherwise check if event includes the ability to get the modifier state + else if(typeof event === "object" && event !== null && "originalEvent" in event === true && typeof event["originalEvent"] === "object" && event["originalEvent"] !== null && "getModifierState" in event["originalEvent"] === true) { + + // Check if caps lock is on + if(event["originalEvent"].getModifierState("CapsLock") === true) { + + // Check if state is off + if(self.state === false) { + + // Set state + self.state = true; + + // Set that body display has caps lock + self.bodyDisplay.addClass("capsLock"); + } + } + + // Otherwise check if caps lock is off + else if(event["originalEvent"].getModifierState("CapsLock") === false) { + + // Check if state is on + if(self.state === true) { + + // Clear state + self.state = false; + + // Set that body display doesn't have caps lock + self.bodyDisplay.removeClass("capsLock"); + } + } + } + }); + } + + // Is on + isOn() { + + // Return if caps lock is on + return this.state === true; + } + + // Is off + isOff() { + + // Return if not on + return this.isOn() === false; + } +} + + +// Main function + +// Set global object's caps lock +globalThis["CapsLock"] = CapsLock; diff --git a/scripts/clipboard.js b/scripts/clipboard.js new file mode 100755 index 0000000..b53e409 --- /dev/null +++ b/scripts/clipboard.js @@ -0,0 +1,48 @@ +// Use strict +"use strict"; + + +// Classes + +// Clipboard class +class Clipboard { + + // Public + + // Copy + copy(text) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if clipboard API is supported + if(typeof navigator === "object" && navigator !== null && "clipboard" in navigator === true) { + + // Return writing text to clipboard + return navigator["clipboard"].writeText(text).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject("Writing to clipboard failed."); + }); + } + + // Otherwise + else + + // Reject error + reject("Clipboard not supported."); + }); + } +} + + +// Main function + +// Set global object's clipboard +globalThis["Clipboard"] = Clipboard; diff --git a/scripts/common.js b/scripts/common.js new file mode 100755 index 0000000..bdb956f --- /dev/null +++ b/scripts/common.js @@ -0,0 +1,1951 @@ +// Use strict +"use strict"; + + +// Classes + +// Common class +class Common { + + // Public + + // From hex string + static fromHexString(hexString, useSharedArrayBuffer = false) { + + // Initialize result + var result = new Uint8Array((useSharedArrayBuffer === true && crossOriginIsolated === true) ? new SharedArrayBuffer(Common.hexStringLength(hexString)) : Common.hexStringLength(hexString)); + + // Go through all bytes in the result + for(var i = 0; i < result["length"]; ++i) { + + // Set character as a byte in the result + result[i] = (Common.HEX_CHARACTER_TO_VALUE[hexString[i * Common.HEX_NUMBER_LENGTH]] << (Common.BITS_IN_A_BYTE / 2)) | Common.HEX_CHARACTER_TO_VALUE[hexString[i * Common.HEX_NUMBER_LENGTH + 1]]; + } + + // Return result + return result; + } + + // To hex string + static toHexString(byteArray) { + + // Initialize result + var result = ""; + + // Go through all bytes in the byte array + for(var i = 0; i < byteArray["length"]; ++i) { + + // Get byte + var byte = byteArray[i]; + + // Append byte as characters to the result + result = result.concat(Common.VALUE_TO_HEX_CHARACTER[byte >>> (Common.BITS_IN_A_BYTE / 2)], Common.VALUE_TO_HEX_CHARACTER[byte & 0x0F]); + } + + // Return result + return result; + } + + // Is hex string + static isHexString(string) { + + // Check if string isn't a string + if(typeof string !== "string") + + // Return false + return false; + + // Check if string's length is invalid + if(string["length"] % Common.HEX_NUMBER_LENGTH !== 0) { + + // Return false + return false; + } + + // Return if string is a hex string + return Common.HEX_STRING_PATTERN.test(string) === true; + } + + // Hex string length + static hexStringLength(string) { + + // Return hex string length + return string["length"] / Common.HEX_NUMBER_LENGTH; + } + + // Is number string + static isNumberString(string) { + + // Check if string isn't a string + if(typeof string !== "string") + + // Return false + return false; + + // Return if string is a number string + return Common.NUMBER_STRING_PATTERN.test(string) === true; + } + + // Is RFC 3339 string + static isRfc3339String(string) { + + // Check if string isn't a string + if(typeof string !== "string") + + // Return false + return false; + + // Check if string isn't an RFC 3339 string + if(Common.RFC_3339_STRING_PATTERN.test(string) === false) + + // Return false + return false; + + // Return if string could be converted from a RFC 3339 string to a timestamp + return Number.isNaN(Common.rfc3339StringToTimestamp(string)) === false; + } + + // Is lowercase string + static isLowercaseString(string) { + + // Check if string isn't a string + if(typeof string !== "string") + + // Return false + return false; + + // Return if string is a a lowercase string + return Common.LOWERCASE_STRING_PATTERN.test(string) === true; + } + + // RFC 3339 string to timestamp + static rfc3339StringToTimestamp(string) { + + // Return timestamp from string + return Date.parse(string); + } + + // Merge arrays + static mergeArrays(arrays) { + + // Initialize result + var result = new Uint8Array([]); + + // Go through all arrays + for(var i = 0; i < arrays["length"]; ++i) { + + // Get array + var array = arrays[i]; + + // Set updated result to be the length of the current result and the array + var updatedResult = new Uint8Array(result["length"] + array["length"]); + + // Set arrays in the updated result + updatedResult.set(result); + updatedResult.set(array, result["length"]); + + // Set result to the updated result + result = updatedResult; + } + + // Return result + return result; + } + + // Arrays are equal + static arraysAreEqual(arrayOne, arrayTwo) { + + // Check if arrays have different lengths + if(arrayOne["length"] !== arrayTwo["length"]) + + // Return false + return false; + + // Go through all values each array + for(var i = 0; i < arrayOne["length"]; ++i) + + // Check if array values differ + if(arrayOne[i] !== arrayTwo[i]) + + // Return false + return false; + + // Return true + return true; + } + + // Arrays are equal timing safe + static arraysAreEqualTimingSafe(arrayOne, arrayTwo) { + + // Initialize results + var result = (arrayOne["length"] === arrayTwo["length"]) ? 0 : 1; + + // Go through all values in the first array + for(var i = 0; i < arrayOne["length"]; ++i) { + + // Get current value from each array + var arrayOneValue = arrayOne[i]; + var arrayTwoValue = (i < arrayTwo["length"]) ? arrayTwo[i] : 0; + + // Update result to if current values are equal + result |= (arrayOneValue === arrayTwoValue) ? 0 : 1; + } + + // Return if arrays are equal + return result === 0; + } + + // Get current timestamp + static getCurrentTimestamp() { + + // Return current timestamp + return Math.floor(Date.now() / Common.MILLISECONDS_IN_A_SECOND); + } + + // Is extension + static isExtension() { + + // Return if extension + return typeof location !== "undefined" && location["protocol"]["length"] > "-extension:"["length"] && location["protocol"].substring(location["protocol"]["length"] - "-extension:"["length"]) === "-extension:"; + } + + // Is popup + static isPopup() { + + // Get URL parameters + var urlParameters = Common.getUrlParameters(); + + // Return if popup + return Common.isExtension() === true && "Is Popup" in urlParameters === true && urlParameters["Is Popup"] === "True"; + } + + // Is app + static isApp() { + + // Return if app + return Common.isExtension() === false && ((typeof navigator === "object" && navigator !== null && "standalone" in navigator === true && navigator["standalone"] === true) || (typeof matchMedia === "function" && matchMedia("(display-mode: standalone)")["matches"] === true)); + } + + // HTML encode + static htmlEncode(string) { + + // Return string with HTML entities encoded + return $("

    ").text(string).html().replace(Common.DOUBLE_QUOTE_PATTERN, Common.DOUBLE_QUOTE_HTML_ENTITY).replace(Common.SINGLE_QUOTE_PATTERN, Common.SINGLE_QUOTE_HTML_ENTITY).replace(Common.GRAVE_ACCENT_PATTERN, Common.GRAVE_ACCENT_HTML_ENTITY); + } + + // HTML decode + static htmlDecode(htmlString) { + + // Return html string with HTML entities decoded + return $("
    ").html(htmlString).text(); + } + + // Random number + static randomNumber(minValue, maxValue) { + + // Return random number between values inclusively + return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue; + } + + // Escape regular expression + static escapeRegularExpression(string) { + + // Return string with regular expression meta characters escaped + return string.replace(/[.*+?^${}()|[\]\\]/ug, "\\$&"); + } + + // Map + static map(value, inMin, inMax, outMin, outMax) { + + // Return value mapped to different range + return (new BigNumber(outMax)).minus(outMin).multipliedBy((new BigNumber(value)).minus(inMin)).dividedBy((new BigNumber(inMax)).minus(inMin)).plus(outMin); + } + + // Is valid URL + static isValidUrl(url) { + + // Check if URL doesn't have a protocol + if(Common.urlContainsProtocol(url) === false) { + + // Add protocol to URL + url = Common.HTTP_PROTOCOL + "//" + url; + } + + // Try + try { + + // Parse URL as a URL + new URL(url); + } + + // Catch errors + catch(error) { + + // Return false + return false; + } + + // Return true + return true; + } + + // Is iframe + static isIframe() { + + // Try + try { + + // Return if self isn't the top window + return window["self"] !== window["top"]; + } + + // Catch errors + catch(error) { + + // Return true + return true; + } + } + + // On reduced data connection + static onReducedDataConnection() { + + // Return if save data is enabled + return typeof navigator === "object" && navigator !== null && "connection" in navigator === true && typeof navigator["connection"] === "object" && navigator["connection"] !== null && "saveData" in navigator["connection"] === true && navigator["connection"]["saveData"] === true; + } + + // Is low memory device + static isLowMemoryDevice() { + + // Return if device has low memory + return typeof navigator === "object" && navigator !== null && "deviceMemory" in navigator === true && navigator["deviceMemory"] <= Common.LOW_MEMORY_THRESHOLD_GIGABYTES; + } + + // Is high memory device + static isHighMemoryDevice() { + + // Return if device has high memory + return typeof navigator === "object" && navigator !== null && "deviceMemory" in navigator === true && navigator["deviceMemory"] >= Common.HIGH_MEMORY_THRESHOLD_GIGABYTES; + } + + // Is mobile device + static isMobileDevice() { + + // Return if device is a mobile device + return typeof navigator === "object" && navigator !== null && "userAgentData" in navigator === true && "mobile" in navigator["userAgentData"] === true && navigator["userAgentData"]["mobile"] === true; + } + + + // Is using cellular network + static isUsingCellularNetwork() { + + // Return if using cellular network + return typeof navigator === "object" && navigator !== null && "connection" in navigator === true && "type" in navigator["connection"] === true && navigator["connection"]["type"] === "cellular"; + } + + // Upgrade applicable insecure URL + static upgradeApplicableInsecureUrl(url) { + + // Try + try { + + // Parse HTTPS server address as a URL + var parsedHttpsServerUrl = new URL(HTTPS_SERVER_ADDRESS); + + // Parse Tor server address as a URL + var parsedTorServerUrl = new URL(TOR_SERVER_ADDRESS); + + // Parse URL as a URL + var parsedUrl = new URL(url); + } + + // Catch errors + catch(error) { + + // Return url + return url; + } + + // Check if URL's protocol is HTTP, the HTTPS server address's protocol is HTTPS, and the URL is the same site as the HTTPS server address + if(parsedUrl["protocol"] === Common.HTTP_PROTOCOL && parsedHttpsServerUrl["protocol"] === Common.HTTPS_PROTOCOL && parsedUrl["hostname"] === parsedHttpsServerUrl["hostname"]) { + + // Create an HTTPS URL from the URL + var httpsUrl = Common.HTTPS_PROTOCOL + Common.ltrim(url).substring(Common.HTTP_PROTOCOL["length"]); + + // Return HTTPS URL + return httpsUrl; + } + + // Otherwise check if URL's protocol is WebSocket, the HTTPS server address's protocol is HTTPS, and the URL is the same site as the HTTPS server address + else if(parsedUrl["protocol"] === Common.WEBSOCKET_PROTOCOL && parsedHttpsServerUrl["protocol"] === Common.HTTPS_PROTOCOL && parsedUrl["hostname"] === parsedHttpsServerUrl["hostname"]) { + + // Create an WebSocket secure URL from the URL + var wssUrl = Common.WEBSOCKET_SECURE_PROTOCOL + Common.ltrim(url).substring(Common.WEBSOCKET_PROTOCOL["length"]); + + // Return WebSocket secure URL + return wssUrl; + } + + // Otherwise check if URL's protocol is HTTPS, the Tor server address's protocol is HTTP, and the URL is the same site as the Tor server address + else if(parsedUrl["protocol"] === Common.HTTPS_PROTOCOL && parsedTorServerUrl["protocol"] === Common.HTTP_PROTOCOL && parsedUrl["hostname"] === parsedTorServerUrl["hostname"]) { + + // Create an HTTP URL from the URL + var httpUrl = Common.HTTP_PROTOCOL + Common.ltrim(url).substring(Common.HTTPS_PROTOCOL["length"]); + + // Return HTTP URL + return httpUrl; + } + + // Otherwise check if URL's protocol is WebSocket secure, the Tor server address's protocol is HTTP, and the URL is the same site as the Tor server address + else if(parsedUrl["protocol"] === Common.WEBSOCKET_SECURE_PROTOCOL && parsedTorServerUrl["protocol"] === Common.HTTP_PROTOCOL && parsedUrl["hostname"] === parsedTorServerUrl["hostname"]) { + + // Create an WebSocket URL from the URL + var wsUrl = Common.WEBSOCKET_PROTOCOL + Common.ltrim(url).substring(Common.WEBSOCKET_SECURE_PROTOCOL["length"]); + + // Return WebSocket URL + return wsUrl; + } + + // Otherwise + else { + + // Return URL + return url; + } + } + + // Get URL parmaters + static getUrlParameters() { + + // Initialize result + var result = {}; + + // Check if URL query string exists + if(typeof URL_QUERY_STRING !== "undefined") { + + // Get URL parameters from URL query string + var urlParameters = URL_QUERY_STRING.substring(Common.URL_QUERY_STRING_SEPARATOR["length"]).split(Common.URL_QUERY_STRING_PARAMETER_SEPARATOR); + + // Go through all URL parameters + for(var i = 0; i < urlParameters["length"]; ++i) { + + // Get URL parameter + var urlParameter = urlParameters[i].split(Common.URL_QUERY_STRING_PARAMETER_VALUE_SEPARATOR); + + // Check if URL parameter is valid + if(urlParameter["length"] === 2) { + + // Get URL parameter's key + var key = decodeURIComponent(urlParameter[0].replace(/\+/ug, "%20")); + + // Check if key isn't already in the rersult + if(key in result === false) { + + // Set URL parameter in the result + result[key] = decodeURIComponent(urlParameter[1].replace(/\+/ug, "%20")); + } + } + } + } + + // Return result + return result; + } + + // URL contains protocol + static urlContainsProtocol(url) { + + // Return if the URL contains protocol + return Common.URL_PROTOCOL_PATTERN.test(url) === true; + } + + // URL contains top-level domain + static urlContainsTopLevelDomain(url) { + + // Return if the URL contains a top-level domain + return Common.URL_TOP_LEVEL_DOMAIN_PATTERN.test(url) === true; + } + + // Remove subdomain + static removeSubdomain(url) { + + // Return URL's without its subdomain + return url.match(Common.URL_DOMAIN_NAME_PATTERN)[0]; + } + + // Remove top-level domain + static removeTopLevelDomain(url) { + + // Return URL without its top-level domain + return url.match(Common.URL_WITHOUT_TOP_LEVEL_DOMAIN_PATTERN)[1]; + } + + // Remove trailing slashes + static removeTrailingSlashes(text) { + + // Return text with its trailing slashes removed + return Common.rtrim(text).replace(Common.TRAILING_SLASHES_PATTERN, ""); + } + + // Remove duplicate slashes + static removeDuplicateSlashes(text) { + + // Return text with its duplicate slashes removed + return text.replace(Common.DUPLICATE_SLASHES_PATTERN, "/"); + } + + // Split remaining + static splitRemaining(text, separator, limit) { + + // Initialzie result + var result = []; + + // Go through limit + for(var i = 0; i < limit; ++i) { + + // Get last separator end + var lastSeparatorEnd = separator["lastIndex"]; + + // Get current separator start + var currentSeparatorStart = separator.exec(text); + + // Check if separator start exists + if(currentSeparatorStart !== Common.NO_MATCH_FOUND) { + + // Append part to result + result.push(text.slice(lastSeparatorEnd, currentSeparatorStart["index"])); + } + + // Otherwise + else { + + // Get last part + var lastPart = text.slice(lastSeparatorEnd); + + // Check if last part exists and isn't a separator + if(lastPart["length"] !== 0 && separator.test(lastPart) === false) { + + // Append last part to result + result.push(lastPart); + } + + // Return result + return result; + } + } + + // Get last part + var lastPart = text.slice(separator["lastIndex"]); + + // Check if last part exists and isn't a separator + if(lastPart["length"] !== 0 && separator.test(lastPart) === false) { + + // Append last part to result + result.push(lastPart); + } + + // Return result + return result; + } + + // Remove duplicate newlines + static removeDuplicateNewlines(text) { + + // Return text with duplicate newlines and trailing newlines removed from it + return text.replace(Common.DUPLICATE_NEWLINE_PATTERN, "\n").replace(Common.TRAILING_NEWLINE_PATTERN, ""); + } + + // Remove trailing zeros + static removeTrailingZeros(text) { + + // Remove trailing zeros and ending decimal point from text + text = text.replace(Common.TRAILING_ZEROS_PATTERN, "$1").replace(Common.ENDING_DECIMAL_POINT_PATTERN, ""); + + // Check if text is invalid + if(Number.isNaN(parseFloat(text)) === true) { + + // Return zero + return "0"; + } + + // Otherwise + else { + + // Return text + return text; + } + } + + // Get number string precision + static getNumberStringPrecision(text) { + + // Remove trailing zeros from text + text = Common.removeTrailingZeros(text); + + // Check if text doesn't contains a fractional component + var fractionIndex = text.indexOf("."); + + if(fractionIndex === Common.INDEX_NOT_FOUND) { + + // Return zero + return 0; + } + + // Otherwise + else { + + // Return number of fractional digits + return text["length"] - (fractionIndex + "."["length"]); + } + } + + // Serialize object + static serializeObject(object) { + + // Check if object is an array + if(Array.isArray(object) === true) { + + // Return serialized object + return { + + // Value + "Value": object.map(function(value) { + + // Return serialized value + return Common.serializeObject(value); + }) + }; + } + + // Otherwise check if object is an object + else if(typeof object === "object" && object !== null) { + + // Check if object is a typed array + if("buffer" in object === true && object["buffer"] instanceof ArrayBuffer === true) { + + // Get object's type + var type = Object["prototype"]["toString"].call(object).match(Common.OBJECT_TYPE_PATTERN); + + // Return serialized object + return { + + // Constructor + "Constructor": (type !== Common.NO_MATCH_FOUND) ? type[1] : object["constructor"]["name"], + + // Value + "Value": Array.from(object) + }; + } + + // Otherwise + else { + + // Return serialized object + return { + + // Constructor + "Constructor": (object instanceof BigNumber === true) ? "BigNumber" : object["constructor"]["name"], + + // Properties + "Properties": Object.entries(object).map(function(property) { + + // Return serialized property + return { + + // Name + "Name": property[0], + + // Value + "Value": Common.serializeObject(property[1]) + }; + }) + }; + } + } + + // Otherwise + else { + + // Return serialized object + return { + + // Value + "Value": object + }; + } + } + + // Unserialize object + static unserializeObject(serializedObject) { + + // Check if serialized object contains a constructor + if("Constructor" in serializedObject === true) { + + // Check if serialized object can be created + if(serializedObject["Constructor"] in globalThis === true) { + + // Check if serialzed object is a typed array + if("Value" in serializedObject === true) { + + // Create object + var object = new globalThis[serializedObject["Constructor"]](serializedObject["Value"]); + + // Return object + return object; + } + + // Otherwise + else { + + // Create object + var object = Object.create(globalThis[serializedObject["Constructor"]]["prototype"]); + + // Go through all of the serialized object's properties + for(var i = 0; i < serializedObject["Properties"]["length"]; ++i) { + + // Get property + var property = serializedObject["Properties"][i]; + + // Set property in the object + object[property["Name"]] = Common.unserializeObject(property["Value"]); + } + + // Return object + return object; + } + } + + // Otherwise + else { + + // Return undefined + return undefined; + } + } + + // Otherwise check if serialzied object is an array + else if(Array.isArray(serializedObject["Value"]) === true) { + + // Return serialized object's value + return serializedObject["Value"].map(function(serializedValue) { + + // Return unserializing the serialized value + return Common.unserializeObject(serializedValue); + }); + } + + // Otherwise + else { + + // Return serialized object's value + return serializedObject["Value"]; + } + } + + // Request animation frame or timeout + static requestAnimationFrameOrTimeout(callback) { + + // Initialize animation frame + var animationFrame; + + // Set timeout + var timeout = setTimeout(function() { + + // Cancel animation frame + cancelAnimationFrame(animationFrame); + + // Run callback + callback(event); + + }, Common.REQUEST_ANIMATION_FRAME_TIMEOUT_MILLISECONDS); + + // Set animation frame + animationFrame = requestAnimationFrame(function(event) { + + // Clear timeout + clearTimeout(timeout); + + // Run callback + callback(event); + }); + } + + // Ltrim + static ltrim(text) { + + // Return text with leading whitespace removed + return text.replace(Common.LEADING_WHITESPACE_PATTERN, ""); + } + + // Rtrim + static rtrim(text) { + + // Return text with trailing whitespace removed + return text.replace(Common.TRAILING_WHITESPACE_PATTERN, ""); + } + + // Get preserved URL parameters + static getPreservedUrlParameters() { + + // Check if is popup + if(Common.isPopup() === true) { + + // Return preserved URL parameters + return Common.URL_QUERY_STRING_PARAMETER_SEPARATOR + encodeURIComponent("Is Popup").replace(/%20/ug, "+") + Common.URL_QUERY_STRING_PARAMETER_VALUE_SEPARATOR + encodeURIComponent("True").replace(/%20/ug, "+"); + } + + // Otherwise + else { + + // Return nothing + return ""; + } + } + + // Save file + static saveFile(name, contents) { + + // Create anchor + var anchor = $(""); + + // Create URL from contents + var url = URL.createObjectURL(new Blob([ + + // Contents + contents + ], { + + // Type + "type": "application/octet-stream" + })); + + // Set anchor's href to URL + anchor.attr("href", url); + + // Set anchor's download to name + anchor.attr("download", name); + + // Click on anchor + anchor.get(0).click(); + + // Set timeout + setTimeout(function() { + + // Revoke URL + URL.revokeObjectURL(url); + }, 0); + } + + // Has whitespace + static hasWhitespace(text) { + + // Return if text has whitespace + return Common.HAS_WHITESPACE_PATTERN.test(text) === true; + } + + // Remove whitespace + static removeWhitespace(text) { + + // Return text with whitespace removed + return text.replace(Common.HAS_WHITESPACE_PATTERN, ""); + } + + // Milliseconds in a second + static get MILLISECONDS_IN_A_SECOND() { + + // Return milliseconds in a second + return 1000; + } + + // Seconds in a minute + static get SECONDS_IN_A_MINUTE() { + + // Return minutes in a second + return 60; + } + + // Minutes in an hour + static get MINUTES_IN_AN_HOUR() { + + // Return minutes in an hour + return 60; + } + + // Hours in a day + static get HOURS_IN_A_DAY() { + + // Return hours in a day + return 24; + } + + // Days in a week + static get DAYS_IN_A_WEEK() { + + // Return days in a week + return 7; + } + + // Weeks in a year + static get WEEKS_IN_A_YEAR() { + + // Return weeks in a year + return 52; + } + + // Byte max value + static get BYTE_MAX_VALUE() { + + // Return byte max value + return Math.pow(2, Common.BITS_IN_A_BYTE) - 1; + } + + // Uint16 max value + static get UINT16_MAX_VALUE() { + + // Return uint16 max value + return Math.pow(2, Common.BITS_IN_A_BYTE * Uint16Array["BYTES_PER_ELEMENT"]) - 1; + } + + // Uint32 max value + static get UINT32_MAX_VALUE() { + + // Return uint32 max value + return Math.pow(2, Common.BITS_IN_A_BYTE * Uint32Array["BYTES_PER_ELEMENT"]) - 1; + } + + // Int32 max value + static get INT32_MAX_VALUE() { + + // Return int32 max value + return Math.pow(2, Common.BITS_IN_A_BYTE * Int32Array["BYTES_PER_ELEMENT"] - 1) - 1; + } + + // Bytes in a uint8 + static get BYTES_IN_A_UINT8() { + + // Return bytes in a uint8 + return 8 / Common.BITS_IN_A_BYTE; + } + + // Bytes in a uint16 + static get BYTES_IN_A_UINT16() { + + // Return bytes in a uint16 + return 16 / Common.BITS_IN_A_BYTE; + } + + // Bytes in a uint32 + static get BYTES_IN_A_UINT32() { + + // Return bytes in a uint32 + return 32 / Common.BITS_IN_A_BYTE; + } + + // Bytes in a uint64 + static get BYTES_IN_A_UINT64() { + + // Return bytes in a uint64 + return 64 / Common.BITS_IN_A_BYTE; + } + + // Invalid local storage item + static get INVALID_LOCAL_STORAGE_ITEM() { + + // Return invalid local storage item + return null; + } + + // Index not found + static get INDEX_NOT_FOUND() { + + // Return index not found + return -1; + } + + // No match found + static get NO_MATCH_FOUND() { + + // Return no match found + return null; + } + + // No tab index + static get NO_TAB_INDEX() { + + // Return no tab index + return "-1"; + } + + // Decimal number base + static get DECIMAL_NUMBER_BASE() { + + // Return decimal number base + return 10; + } + + // Hex number base + static get HEX_NUMBER_BASE() { + + // Return hex number base + return 16; + } + + // Hex number length + static get HEX_NUMBER_LENGTH() { + + // Return hex number length + return "FF"["length"]; + } + + // Hex color length + static get HEX_COLOR_LENGTH() { + + // Return hex color length + return "FFFFFF"["length"]; + } + + // Hex number padding + static get HEX_NUMBER_PADDING() { + + // Return hex number padding + return "0"; + } + + // Focus change event + static get FOCUS_CHANGE_EVENT() { + + // Return focus change event + return "FocusChangeEvent"; + } + + // No attribute + static get NO_ATTRIBUTE() { + + // Return no attribute + return undefined; + } + + // No argument + static get NO_ARGUMENT() { + + // Return no argument + return undefined; + } + + // URL query string separator + static get URL_QUERY_STRING_SEPARATOR() { + + // Return URL query string separator + return "?"; + } + + // URL query string parameter separator + static get URL_QUERY_STRING_PARAMETER_SEPARATOR() { + + // Return URL query string parameter separator + return "&"; + } + + // URL query string parameter value separator + static get URL_QUERY_STRING_PARAMETER_VALUE_SEPARATOR() { + + // Return URL query string parameter value separator + return "="; + } + + // Data attribute prefix + static get DATA_ATTRIBUTE_PREFIX() { + + // Return data attribute prefix + return "data-"; + } + + // First match result index + static get FIRST_MATCH_RESULT_INDEX() { + + // Return first match result index + return 1; + } + + // JSON NULL value + static get JSON_NULL_VALUE() { + + // Return JSON NULL value + return null; + } + + // HTTP protocol + static get HTTP_PROTOCOL() { + + // Return HTTP protocol + return "http:"; + } + + // HTTPS protocol + static get HTTPS_PROTOCOL() { + + // Return HTTPS protocol + return "https:"; + } + + // WebSocket protocol + static get WEBSOCKET_PROTOCOL() { + + // Return WebSocket protocol + return "ws:"; + } + + // WebSocket secure protocol + static get WEBSOCKET_SECURE_PROTOCOL() { + + // Return WebSocket secure protocol + return "wss:"; + } + + // File protocol + static get FILE_PROTOCOL() { + + // Return file protocol + return "file:"; + } + + // HTTP no response status + static get HTTP_NO_RESPONSE_STATUS() { + + // Return HTTP no response status + return 0; + } + + // HTTP ok status + static get HTTP_OK_STATUS() { + + // Return HTTP ok status + return 200; + } + + // HTTP bad request status + static get HTTP_BAD_REQUEST_STATUS() { + + // Return HTTP bad request status + return 400; + } + + // HTTP unauthorized status + static get HTTP_UNAUTHORIZED_STATUS() { + + // Return HTTP unauthorized status + return 401; + } + + // HTTP forbidden status + static get HTTP_FORBIDDEN_STATUS() { + + // Return HTTP forbidden status + return 403; + } + + // HTTP not found status + static get HTTP_NOT_FOUND_STATUS() { + + // Return HTTP not found status + return 404; + } + + // HTTP payload too large status + static get HTTP_PAYLOAD_TOO_LARGE_STATUS() { + + // Return HTTP payload too large status + return 413; + } + + // HTTP unsupported media type status + static get HTTP_UNSUPPORTED_MEDIA_TYPE_STATUS() { + + // Return HTTP unsupported media type status + return 415; + } + + // HTTP bad gateway status + static get HTTP_BAD_GATEWAY_STATUS() { + + // Return HTTP bad gateway status + return 502; + } + + // HTTP gateway timeout status + static get HTTP_GATEWAY_TIMEOUT_STATUS() { + + // Return HTTP gateway timeout status + return 504; + } + + // Double quote pattern + static get DOUBLE_QUOTE_PATTERN() { + + // Return double quote pattern + return /"/ug; + } + + // Enable event + static get ENABLE_EVENT() { + + // Return enable event + return "CommonEnableEvent"; + } + + // Primary pointer button bitmask + static get PRIMARY_POINTER_BUTTON_BITMASK() { + + // Return primary pointer button bitmask + return 1; + } + + // January month index + static get JANUARY_MONTH_INDEX() { + + // Return january month index + return 0; + } + + // Sort equal to + static get SORT_EQUAL() { + + // Return sort equal + return 0; + } + + // Sort greater than + static get SORT_GREATER_THAN() { + + // Return sort greater than + return Common.SORT_EQUAL + 1; + } + + // Sort less than + static get SORT_LESS_THAN() { + + // Return sort less than + return Common.SORT_EQUAL - 1; + } + + // Visibility state visible + static get VISIBILITY_STATE_VISIBLE() { + + // Return visibility state visible + return "visible"; + } + + // Visibility state hidden + static get VISIBILITY_STATE_HIDDEN() { + + // Return visibility state hidden + return "hidden"; + } + + // Canceled error + static get CANCELED_ERROR() { + + // Return canceled error + return "CommonCancelError"; + } + + // No cancel occurred + static get NO_CANCEL_OCCURRED() { + + // Return no cancel occurred + return null; + } + + // Hex prefix + static get HEX_PREFIX() { + + // Return hex prefix + return "0x"; + } + + // Default HTTP port + static get DEFAULT_HTTP_PORT() { + + // Return default HTTP port + return 80; + } + + // Default HTTPS port + static get DEFAULT_HTTPS_PORT() { + + // Return default HTTPS port + return 443; + } + + // Private + + // Bits in a byte + static get BITS_IN_A_BYTE() { + + // Return bits in a byte + return 8; + } + + // Number string pattern + static get NUMBER_STRING_PATTERN() { + + // Return number string pattern + return /^[+-]?(?:0(?:\.\d+)?|[1-9]\d*(?:\.\d+)?|\.\d+)$/u; + } + + // Hex string pattern + static get HEX_STRING_PATTERN() { + + // Return hex string pattern + return /^(?:[0-9A-F])+$/iu; + } + + // Hex character to value + static get HEX_CHARACTER_TO_VALUE() { + + // Return hex character to value + return { + + // Zero + "0": 0x00, + + // One + "1": 0x01, + + // Two + "2": 0x02, + + // Three + "3": 0x03, + + // Four + "4": 0x04, + + // Five + "5": 0x05, + + // Six + "6": 0x06, + + // Seven + "7": 0x07, + + // Eight + "8": 0x08, + + // Nine + "9": 0x09, + + // Uppercase a + "A": 0x0A, + + // Uppercase b + "B": 0x0B, + + // Uppercase c + "C": 0x0C, + + // Uppercase d + "D": 0x0D, + + // Uppercase e + "E": 0x0E, + + // Uppercase f + "F": 0x0F, + + // Lowercase a + "a": 0x0A, + + // Lowercase b + "b": 0x0B, + + // Lowercase c + "c": 0x0C, + + // Lowercase d + "d": 0x0D, + + // Lowercase e + "e": 0x0E, + + // Lowercase f + "f": 0x0F + }; + } + + // Value to hex character + static get VALUE_TO_HEX_CHARACTER() { + + // Return value to hex character + return [ + + // Zero + "0", + + // One + "1", + + // Two + "2", + + // Three + "3", + + // Four + "4", + + // Five + "5", + + // Six + "6", + + // Seven + "7", + + // Eight + "8", + + // Nine + "9", + + // A + "a", + + // B + "b", + + // C + "c", + + // D + "d", + + // E + "e", + + // F + "f" + ]; + } + + // RFC 3339 string pattern + static get RFC_3339_STRING_PATTERN() { + + // Return RFC 3339 string pattern + return /^(?:\d+)-(?:0[1-9]|1[012])-(?:0[1-9]|[12]\d|3[01])[Tt](?:[01]\d|2[0-3]):(?:[0-5]\d):(?:[0-5]\d|60)(?:\.\d+)?(?:(?:[Zz])|(?:[\+|\-](?:[01]\d|2[0-3]):[0-5]\d))$/u; + } + + // Lowercase string pattern + static get LOWERCASE_STRING_PATTERN() { + + // Return lowercase string pattern + return /^[a-z0-9]+$/u; + } + + // Single quote HTML entity + static get SINGLE_QUOTE_HTML_ENTITY() { + + // Return single quote HTML entity + return "'"; + } + + // Double quote HTML entity + static get DOUBLE_QUOTE_HTML_ENTITY() { + + // Return double quote HTML entity + return """; + } + + // Grave accent HTML entity + static get GRAVE_ACCENT_HTML_ENTITY() { + + // Return grave accent HTML entity + return "`"; + } + + // Single quote pattern + static get SINGLE_QUOTE_PATTERN() { + + // Return single quote pattern + return /'/ug; + } + + // Grave accent pattern + static get GRAVE_ACCENT_PATTERN() { + + // Return grave accent pattern + return /`/ug; + } + + // Return low memory threshold gigabytes + static get LOW_MEMORY_THRESHOLD_GIGABYTES() { + + // Return low memory threshold gigabytes + return 2; + } + + // Return high memory threshold gigabytes + static get HIGH_MEMORY_THRESHOLD_GIGABYTES() { + + // Return high memory threshold gigabytes + return 8; + } + + // URL protocol pattern + static get URL_PROTOCOL_PATTERN() { + + // Return URL protocol pattern + return /^[^:]+:\/\/.+/u; + } + + // URL top-level domain pattern + static get URL_TOP_LEVEL_DOMAIN_PATTERN() { + + // Return URL top-level domain pattern + return /[^\.]+\.[^\/?:#\s]+(?:[\/?:#\s]|$)/u; + } + + // URL domain name pattern + static get URL_DOMAIN_NAME_PATTERN() { + + // Return URL domain name pattern + return /[^\.]+\.[^\.]+$/u; + } + + // URL without top-level domain pattern + static get URL_WITHOUT_TOP_LEVEL_DOMAIN_PATTERN() { + + // Return URL without top-level domain pattern + return /([^\.]+)\.[^\.]+$/u; + } + + // Trailing slashes pattern + static get TRAILING_SLASHES_PATTERN() { + + // Return trailing slashes pattern + return /\/+$/u; + } + + // Duplicate slashes pattern + static get DUPLICATE_SLASHES_PATTERN() { + + // Return duplicate slashes pattern + return /\/+/ug; + } + + // Duplicate newline pattern + static get DUPLICATE_NEWLINE_PATTERN() { + + // Return duplicate newline pattern + return /\n+/ug; + } + + // Trailing zeros pattern + static get TRAILING_ZEROS_PATTERN() { + + // Return trailing zeros pattern + return /(\.\d*?)0+$/u; + } + + // Ending decimal point pattern + static get ENDING_DECIMAL_POINT_PATTERN() { + + // Return ending decimal point pattern + return /\.$/u; + } + + // Trailing newline pattern + static get TRAILING_NEWLINE_PATTERN() { + + // Return trailing newline pattern + return /\n$/u; + } + + // Object type pattern + static get OBJECT_TYPE_PATTERN() { + + // Return object type pattern + return /^\[object\s*(.*)\]$/u; + } + + // Request animation frame timeout milliseconds + static get REQUEST_ANIMATION_FRAME_TIMEOUT_MILLISECONDS() { + + // Return request animation frame timeout milliseconds + return 100; + } + + // Leading whitespace pattern + static get LEADING_WHITESPACE_PATTERN() { + + // Return leading whitespace pattern + return /^\s+/u; + } + + // Trailing whitespace pattern + static get TRAILING_WHITESPACE_PATTERN() { + + // Return leading whitespace pattern + return /\s+$/u; + } + + // Has whitespace pattern + static get HAS_WHITESPACE_PATTERN() { + + // Return has whitespace pattern + return /\s/ug; + } +} + + +// Main function + +// Set global object's common +globalThis["Common"] = Common; + +// Check if jQuery exists +if(typeof jQuery === "function") { + + // Disable tab + $["prototype"].disableTab = function() { + + // Go through each element + this.each(function() { + + // Add no tab index to element + $(this).attr("tabindex", Common.NO_TAB_INDEX); + }); + + // Return elements + return this; + }; + + // Enable tab + $["prototype"].enableTab = function() { + + // Go through each element + this.each(function() { + + // Remove tab index from element + $(this).removeAttr("tabindex"); + }); + + // Return elements + return this; + }; + + // Disable + $["prototype"].disable = function() { + + // Go through each element + this.each(function() { + + // Disable the element + $(this).prop("disabled", true); + }); + + // Return elements + return this; + }; + + // Enable + $["prototype"].enable = function() { + + // Go through each element + this.each(function() { + + // Enable the element and trigger an enable event on the element + $(this).prop("disabled", false).trigger(Common.ENABLE_EVENT); + }); + + // Return elements + return this; + }; + + // Outer Html + $["prototype"].outerHtml = function() { + + // Return element's outer HTML + return this.wrap("
    ").parent().html(); + }; + + // Scroll idle duration milliseconds + var SCROLL_IDLE_DURATION_MILLISECONDS = 250; + + // Scroll stopped + $["fn"].scrollStopped = function(callback) { + + // Go through each element + this.each(function() { + + // Set self + var self = $(this); + + // Get index + var index = (typeof self.data("scrollStoppedIndex") === "undefined") ? 0 : self.data("scrollStoppedIndex"); + + // Self scroll scroll stopped index event + self.on("scroll.scrollStopped" + index.toFixed(), function(event) { + + // Check if scroll stopped timeout index exists + if(typeof self.data("scrollStoppedTimeout" + index.toFixed()) !== "undefined") { + + // Clear scroll stopped timeout index + clearTimeout(self.data("scrollStoppedTimeout" + index.toFixed())); + } + + // Set scroll stopped timeout index + self.data("scrollStoppedTimeout" + index.toFixed(), setTimeout(function() { + + // Remove scroll stopped timeout index + self.removeData("scrollStoppedTimeout" + index.toFixed()); + + // Run callback + callback.bind(self)(event); + + }, SCROLL_IDLE_DURATION_MILLISECONDS)); + }); + + // Increment index + self.data("scrollStoppedIndex", (index === Number.MAX_SAFE_INTEGER) ? 0 : index + 1); + }); + + // Return elements + return this; + }; + + // Transition end timeout milliseconds + var TRANSITION_END_TIMEOUT_MILLISECONDS = 100; + + // Transition end or timeout + $["fn"].transitionEndOrTimeout = function(callback, property) { + + // Go through each element + this.each(function() { + + // Set self + var self = $(this); + + // Set timeout + var timeout = TRANSITION_END_TIMEOUT_MILLISECONDS; + + // Go through all of the element's transition properties + var properties = self.css("transition-property").split(","); + for(var i = 0; i < properties["length"]; ++i) { + + // Check if property was found + if(properties[i].trim() === property) { + + // Get transition's duration + var duration = self.css("transition-duration").split(",")[i].trim(); + + // Get transition's delay + var delay = self.css("transition-delay").split(",")[i].trim(); + + // Set timeout to the duration + timeout = parseFloat(duration) * ((duration.indexOf("ms") !== Common.INDEX_NOT_FOUND) ? 1 : Common.MILLISECONDS_IN_A_SECOND) + parseFloat(delay) * ((delay.indexOf("ms") !== Common.INDEX_NOT_FOUND) ? 1 : Common.MILLISECONDS_IN_A_SECOND) + TRANSITION_END_TIMEOUT_MILLISECONDS; + + // Break + break; + } + } + + // Get index + var index = (typeof self.data("transitionEndOrTimeoutIndex") === "undefined") ? 0 : self.data("transitionEndOrTimeoutIndex"); + + // Set transition end or timeout timeout index + self.data("transitionEndOrTimeoutTimeout" + index.toFixed(), setTimeout(function() { + + // Turn off transition end transition end or timeout index event + self.off("transitionend.transitionEndOrTimeout" + index.toFixed()); + + // Remove transition end or timeout timeout index + self.removeData("transitionEndOrTimeoutTimeout" + index.toFixed()); + + // Run callback + callback.bind(self)(); + + }, timeout)); + + // Self transition end transition end or timeout index event + self.one("transitionend.transitionEndOrTimeout" + index.toFixed(), function(event) { + + // Clear transition end or timeout timeout index + clearTimeout(self.data("transitionEndOrTimeoutTimeout" + index.toFixed())); + + // Remove transition end or timeout timeout index + self.removeData("transitionEndOrTimeoutTimeout" + index.toFixed()); + + // Run callback + callback.bind(self)(event); + }); + + // Increment index + self.data("transitionEndOrTimeoutIndex", (index === Number.MAX_SAFE_INTEGER) ? 0 : index + 1); + }); + + // Return elements + return this; + }; + + // Off transition end or timeout + $["fn"].offTransitionEndOrTimeout = function() { + + // Go through each element + this.each(function() { + + // Set self + var self = $(this); + + // Turn off transition end event + self.off("transitionend"); + + // Get data + var data = self.data(); + + // Go through all data + for(var key in data) { + + if(data.hasOwnProperty(key) === true) { + + // Check if data is a transition end or timeout timeout index + if(key.indexOf("transitionEndOrTimeoutTimeout") === 0) { + + // Clear transition end or timeout timeout index + clearTimeout(self.data(key)); + + // Remove transition end or timeout timeout index + self.removeData(key); + } + } + } + }); + + // Return elements + return this; + }; +} + +// Check if BigNumber exists +if(typeof BigNumber === "function") { + + // Big endian + BigNumber.BIG_ENDIAN = 0; + + // Little endian + BigNumber.LITTLE_ENDIAN = 1; + + // Any length + BigNumber.ANY_LENGTH = -1; + + // To bytes + BigNumber.prototype.toBytes = function(endianness = BigNumber.LITTLE_ENDIAN, length = BigNumber.ANY_LENGTH) { + + // Check if is number isn't supported + if(this.isFinite() === false || this.isNaN() === true || this.isNegative() === true || this.isInteger() === false) { + + // Throw error + throw "Unsupported number."; + } + + // Otherwise + else { + + // Create bytes + var bytes = new Array((length === BigNumber.ANY_LENGTH) ? 1 : length).fill(0); + + // Make number a whole number + var temp = this.dividedToIntegerBy(1); + + // Go through all bytes in the whole number + for(var i = 0; temp.isGreaterThan(0); ++i) { + + // Get byte from the whole number + var byte = temp.modulo(Common.BYTE_MAX_VALUE + 1).toNumber(); + + // Remove byte from the whole number + temp = temp.dividedToIntegerBy(Common.BYTE_MAX_VALUE + 1); + + // Check if space exists in bytes for the byte + if(i < bytes["length"]) { + + // Set byte in bytes + bytes[i] = byte; + } + + // Otherwise check if length is Constrained + else if(length !== BigNumber.ANY_LENGTH) { + + // Throw error + throw "Insufficient length."; + } + + // Otherwise + else { + + // Append byte to bytes + bytes.push(byte); + } + } + + // Check if endianness is big endian + if(endianness === BigNumber.BIG_ENDIAN) { + + // Reverse bytes order + bytes = bytes.reverse(); + } + + // Return bytes + return new Uint8Array(bytes); + } + }; +} + +// Check if object exists +if(typeof Object === "function") { + + // Is object + Object.isObject = function(variable) { + + // Try + try { + + // Return if variable is an object + return Object.getPrototypeOf(variable)["constructor"]["name"] === "Object"; + } + + // Catch errors + catch(error) { + + // Return false + return false; + } + }; +} + +// Check if Uint8Array exist +if(typeof Uint8Array === "function") { + + // Read uint32 little endian + Uint8Array["prototype"].readUInt32LE = function(index) { + + // Return value at index as a uint32 in little endian + return this[index] | (this[index + 1] << Common.BITS_IN_A_BYTE) | (this[index + 2] << (Common.BITS_IN_A_BYTE * 2)) | (this[index + 3] << (Common.BITS_IN_A_BYTE * 3)); + }; + + // Read uint16 little endian + Uint8Array["prototype"].readUInt16LE = function(index) { + + // Return value at index as a uint16 in little endian + return this[index] | (this[index + 1] << Common.BITS_IN_A_BYTE); + }; +} + +// Check if cross-origin isolated doesn't exist +if(typeof crossOriginIsolated === "undefined") { + + // Set cross-origin isolated to false + var crossOriginIsolated = false; +} diff --git a/scripts/consensus.js b/scripts/consensus.js new file mode 100755 index 0000000..4eda8fe --- /dev/null +++ b/scripts/consensus.js @@ -0,0 +1,1587 @@ +// Use strict +"use strict"; + + +// Classes + +// Consensus class +class Consensus { + + // Public + + // Initialize + static initialize() { + + // Update saved wallet type and network type + Consensus.getWalletType(); + Consensus.getNetworkType(); + } + + // Get wallet type + static getWalletType() { + + // Check if consensus's wallet type doesn't exist + if(typeof Consensus.walletType === "undefined") { + + // Check if not not a web worker and local storage is usable + if((typeof self === "undefined" || typeof WorkerGlobalScope === "undefined" || self instanceof WorkerGlobalScope === false) && typeof localStorage !== "undefined") { + + // Get the saved wallet type + var walletType = localStorage.getItem(Consensus.WALLET_TYPE_LOCAL_STORAGE_NAME); + + // Check if wallet type doesn't exist or isn't valid + if(walletType === Common.INVALID_LOCAL_STORAGE_ITEM || Number.isNaN(parseInt(walletType, Common.DECIMAL_NUMBER_BASE)) === true) { + + // Set wallet type to the default wallet type + walletType = Consensus.DEFAULT_WALLET_TYPE; + } + + // Otherwise + else { + + // Get wallet type in the correct format + walletType = parseInt(walletType, Common.DECIMAL_NUMBER_BASE); + + // Check if the wallet type doesn't exist + if(walletType < Consensus.MWC_WALLET_TYPE || walletType > Consensus.EPIC_WALLET_TYPE) { + + // Set wallet type to the default wallet type + walletType = Consensus.DEFAULT_WALLET_TYPE; + } + } + } + + // Otherwise + else { + + // Set wallet type to the default wallet type + var walletType = Consensus.DEFAULT_WALLET_TYPE; + } + + // Get URL parameters + var urlParameters = Common.getUrlParameters(); + + // Check if override wallet type exists + if(Consensus.OVERRIDE_WALLET_TYPE_URL_PARAMETER_NAME in urlParameters === true) { + + // Check override wallet type + switch(urlParameters[Consensus.OVERRIDE_WALLET_TYPE_URL_PARAMETER_NAME]) { + + // MWC wallet + case Consensus.WALLET_MWC_TEXT_VALUE: + + // Set wallet type to MWC wallet type + walletType = Consensus.MWC_WALLET_TYPE; + + // Break + break; + + // GRIN wallet + case Consensus.WALLET_GRIN_TEXT_VALUE: + + // Set wallet type to GRIN wallet type + walletType = Consensus.GRIN_WALLET_TYPE; + + // Break + break; + + // EPIC wallet + case Consensus.WALLET_EPIC_TEXT_VALUE: + + // Set wallet type to EPIC wallet type + walletType = Consensus.EPIC_WALLET_TYPE; + + // Break + break; + } + } + + // Check if not not a web worker and local storage is usable + if((typeof self === "undefined" || typeof WorkerGlobalScope === "undefined" || self instanceof WorkerGlobalScope === false) && typeof localStorage !== "undefined") { + + // Check if saved wallet type needs to be updated + if(walletType.toFixed() !== localStorage.getItem(Consensus.WALLET_TYPE_LOCAL_STORAGE_NAME)) { + + // Try + try { + + // Save wallet type in local storage + localStorage.setItem(Consensus.WALLET_TYPE_LOCAL_STORAGE_NAME, walletType.toFixed()); + } + + // Catch errors + catch(error) { + + } + } + } + + // Set consensus's wallet type + Consensus.walletType = walletType; + } + + // Return consensus's wallet type + return Consensus.walletType; + } + + // Get network type + static getNetworkType() { + + // Check if consensus's network type doesn't exist + if(typeof Consensus.networkType === "undefined") { + + // Check if not not a web worker and local storage is usable + if((typeof self === "undefined" || typeof WorkerGlobalScope === "undefined" || self instanceof WorkerGlobalScope === false) && typeof localStorage !== "undefined") { + + // Get the saved network type + var networkType = localStorage.getItem(Consensus.NETWORK_TYPE_LOCAL_STORAGE_NAME); + + // Check if network type doesn't exist or isn't valid + if(networkType === Common.INVALID_LOCAL_STORAGE_ITEM || Number.isNaN(parseInt(networkType, Common.DECIMAL_NUMBER_BASE)) === true) { + + // Set network type to the default network type + networkType = Consensus.DEFAULT_NETWORK_TYPE; + } + + // Otherwise + else { + + // Get network type in the correct format + networkType = parseInt(networkType, Common.DECIMAL_NUMBER_BASE); + + // Check if the network type doesn't exist + if(networkType < Consensus.MAINNET_NETWORK_TYPE || networkType > Consensus.TESTNET_NETWORK_TYPE) { + + // Set network type to the default network type + networkType = Consensus.DEFAULT_NETWORK_TYPE; + } + } + } + + // Otherwise + else { + + // Set network type to the default network type + var networkType = Consensus.DEFAULT_NETWORK_TYPE; + } + + // Get URL parameters + var urlParameters = Common.getUrlParameters(); + + // Check if override network type exists + if(Consensus.OVERRIDE_NETWORK_TYPE_URL_PARAMETER_NAME in urlParameters === true) { + + // Check override network type + switch(urlParameters[Consensus.OVERRIDE_NETWORK_TYPE_URL_PARAMETER_NAME]) { + + // Mainnet network + case Consensus.NETWORK_MAINNET_TEXT_VALUE: + + // Set network type to mainnet network type + networkType = Consensus.MAINNET_NETWORK_TYPE; + + // Break + break; + + // Testnet network + case Consensus.NETWORK_TESTNET_TEXT_VALUE: + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Set network type to testnet network type + networkType = Consensus.TESTNET_NETWORK_TYPE; + + // Break + break; + } + + // Break + break; + + // Floonet network + case Consensus.NETWORK_FLOONET_TEXT_VALUE: + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Set network type to testnet network type + networkType = Consensus.TESTNET_NETWORK_TYPE; + + // Break + break; + } + + // Break + break; + } + } + + // Check if not not a web worker and local storage is usable + if((typeof self === "undefined" || typeof WorkerGlobalScope === "undefined" || self instanceof WorkerGlobalScope === false) && typeof localStorage !== "undefined") { + + // Check if saved network type needs to be updated + if(networkType.toFixed() !== localStorage.getItem(Consensus.NETWORK_TYPE_LOCAL_STORAGE_NAME)) { + + // Try + try { + + // Save network type in local storage + localStorage.setItem(Consensus.NETWORK_TYPE_LOCAL_STORAGE_NAME, networkType.toFixed()); + } + + // Catch errors + catch(error) { + + } + } + } + + // Set consensus's network type + Consensus.networkType = networkType; + } + + // Return consensus's network type + return Consensus.networkType; + } + + // Wallet type to text + static walletTypeToText(walletType) { + + // Check wallet type + switch(walletType) { + + // MWC wallet type + case Consensus.MWC_WALLET_TYPE: + + // Return MWC wallet text value + return Consensus.WALLET_MWC_TEXT_VALUE; + + // GRIN wallet type + case Consensus.GRIN_WALLET_TYPE: + + // Return GRIN wallet text value + return Consensus.WALLET_GRIN_TEXT_VALUE; + + // EPIC wallet type + case Consensus.EPIC_WALLET_TYPE: + + // Return EPIC wallet text value + return Consensus.WALLET_EPIC_TEXT_VALUE; + } + } + + // Network type to text + static networkTypeToText(networkType) { + + // Check network type + switch(networkType) { + + // Mainnet network type + case Consensus.MAINNET_NETWORK_TYPE: + + // Return mainnet network text value + return Consensus.NETWORK_MAINNET_TEXT_VALUE; + + // Testnet network type + case Consensus.TESTNET_NETWORK_TYPE: + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or EPIC wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Return floonet network text value + return Consensus.NETWORK_FLOONET_TEXT_VALUE; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return testnet network text value + return Consensus.NETWORK_TESTNET_TEXT_VALUE; + } + } + } + + // Get reward + static getReward(isMainnet, fees, height) { + + // Get block reward at height + var reward = Consensus.getBlockReward(isMainnet, height); + + // Add fee to reward + reward = reward.plus(fees); + + // Return reward + return reward; + } + + // Is valid header version + static isValidHeaderVersion(isMainnet, height, version) { + + // Return if version is valid at height while taking hard forks into account + return version.isEqualTo(Consensus.getHeaderVersion(isMainnet, height)) === true; + } + + // Is no recent duplicate kernel enabled + static isNoRecentDuplicateKernelsEnabled(isMainnet) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if is mainnet + if(isMainnet === true) { + + // Return false + return false; + } + + // Otherwise + else { + + // TODO Support no recent duplicate kernels + return false; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check if is mainnet + if(isMainnet === true) { + + // Return false + return false; + } + + // Otherwise + else { + + // TODO Support no recent duplicate kernels + return false; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return false + return false; + } + } + + // Block time seconds + static get BLOCK_TIME_SECONDS() { + + // Return block time seconds + return 60; + } + + // Block height minute + static get BLOCK_HEIGHT_MINUTE() { + + // Return block height minute + return Math.floor(Common.SECONDS_IN_A_MINUTE / Consensus.BLOCK_TIME_SECONDS); + } + + // Block height hour + static get BLOCK_HEIGHT_HOUR() { + + // Return block height hour + return Common.MINUTES_IN_AN_HOUR * Consensus.BLOCK_HEIGHT_MINUTE; + } + + // Block height day + static get BLOCK_HEIGHT_DAY() { + + // Return block height day + return Common.HOURS_IN_A_DAY * Consensus.BLOCK_HEIGHT_HOUR; + } + + // Block height week + static get BLOCK_HEIGHT_WEEK() { + + // Return block height week + return Common.DAYS_IN_A_WEEK * Consensus.BLOCK_HEIGHT_DAY; + } + + // Block height year + static get BLOCK_HEIGHT_YEAR() { + + // Return block height year + return Common.WEEKS_IN_A_YEAR * Consensus.BLOCK_HEIGHT_WEEK; + } + + // Coinbase maturity + static get COINBASE_MATURITY() { + + // Return coinbase maturity + return Consensus.BLOCK_HEIGHT_DAY; + } + + // First block height + static get FIRST_BLOCK_HEIGHT() { + + // Return first block height + return 0; + } + + // Value number base + static get VALUE_NUMBER_BASE() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Return value number base + return 1E9; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return value number base + return 1E8; + } + } + + // Currency name + static get CURRENCY_NAME() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Return currency name + return "MWC"; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return currency name + return "GRIN"; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return currency name + return "EPIC"; + } + } + + // Slatepack address human-readable part + static get SLATEPACK_ADDRESS_HUMAN_READABLE_PART() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network type + case Consensus.MAINNET_NETWORK_TYPE: + + // Return Slatepack address human-readable part + return "grin"; + + // Testnet network type + case Consensus.TESTNET_NETWORK_TYPE: + + // Return Slatepack address human-readable part + return "tgrin"; + } + } + } + + // MWC wallet type + static get MWC_WALLET_TYPE() { + + // Return MWC wallet type + return 0; + } + + // GRIN wallet type + static get GRIN_WALLET_TYPE() { + + // Return GRIN wallet type + return Consensus.MWC_WALLET_TYPE + 1; + } + + // EPIC wallet type + static get EPIC_WALLET_TYPE() { + + // Return EPIC wallet type + return Consensus.GRIN_WALLET_TYPE + 1; + } + + // Mainnet network type + static get MAINNET_NETWORK_TYPE() { + + // Return mainnet network type + return 0; + } + + // Testnet type + static get TESTNET_NETWORK_TYPE() { + + // Return testnet network type + return Consensus.MAINNET_NETWORK_TYPE + 1; + } + + // Legacy header version + static get LEGACY_HEADER_VERSION() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC or GRIN wallet + case Consensus.MWC_WALLET_TYPE: + case Consensus.GRIN_WALLET_TYPE: + + // Return legacy header version + return new BigNumber(1); + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return legacy header version + return new BigNumber(6); + } + } + + // Block input weight + static get BLOCK_INPUT_WEIGHT() { + + // Return block input weight + return 1; + } + + // Block output weight + static get BLOCK_OUTPUT_WEIGHT() { + + // Return block output weight + return 21; + } + + // Block kernel weight + static get BLOCK_KERNEL_WEIGHT() { + + // Return block kernel weight + return 3; + } + + // Maximum block weight + static get MAXIMUM_BLOCK_WEIGHT() { + + // Return maximum block weight + return 40000; + } + + // BIP44 coin type + static get BIP44_COIN_TYPE() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 593; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 1; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 592; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 1; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 23000; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return BIP44 coin type + return 1; + } + + // Break + break; + } + } + + // Explorer kernel excess URL + static get EXPLORER_KERNEL_EXCESS_URL() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return explorer kernel excess URL + return "https://explorer.mwc.mw/#k"; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return explorer kernel excess URL + return "https://explorer.floonet.mwc.mw/#k"; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + } + + // Break + break; + } + } + + // Explorer output commitment URL + static get EXPLORER_OUTPUT_COMMITMENT_URL() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return explorer output commitment URL + return "https://explorer.mwc.mw/#o"; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return explorer output commitment URL + return "https://explorer.floonet.mwc.mw/#o"; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return explorer output commitment URL + return "https://explorer.epiccash.com/blockdetail/"; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return no explorer URL + return Consensus.NO_EXPLORER_URL; + } + + // Break + break; + } + } + + // No explorer URL + static get NO_EXPLORER_URL() { + + // Return no explorer URL + return null; + } + + // Hardware waller starting height + static get HARDWARE_WALLET_STARTING_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 1239928; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 1115028; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 1687446; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 1195819; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network + case Consensus.MAINNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 1943415; + + // Testnet network + case Consensus.TESTNET_NETWORK_TYPE: + + // Return hardware wallet starting height + return 0; + } + + // Break + break; + } + } + + // Private + + // Get block reward + static getBlockReward(isMainnet, height) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if height is the first block height + if(height.isEqualTo(Consensus.FIRST_BLOCK_HEIGHT) === true) + + // Return genesis block reward + return new BigNumber("10000000041800000"); + + // Otherwise check if height is less than the second block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 2)) === true) + + // Return reward during the first epoch range + return new BigNumber(2380952380); + + // Otherwise check if height is less than the third block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 3)) === true) + + // Return reward during the second epoch range + return new BigNumber(600000000); + + // Otherwise check if height is less than the fourth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 4)) === true) + + // Return reward during the third epoch range + return new BigNumber(450000000); + + // Otherwise check if height is less than the fifth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 5)) === true) + + // Return reward during the fourth epoch range + return new BigNumber(300000000); + + // Otherwise check if height is less than the sixth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 6)) === true) + + // Return reward during the fifth epoch range + return new BigNumber(250000000); + + // Otherwise check if height is less than the seventh block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 7)) === true) + + // Return reward during the sixth epoch range + return new BigNumber(200000000); + + // Otherwise check if height is less than the eighth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 8)) === true) + + // Return reward during the seventh epoch range + return new BigNumber(150000000); + + // Otherwise check if height is less than the ninth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 9)) === true) + + // Return reward during the eighth epoch range + return new BigNumber(100000000); + + // Otherwise check if height is less than the tenth block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 10)) === true) + + // Return reward during the ninth epoch range + return new BigNumber(50000000); + + // Otherwise check if height is less than the eleventh block offset + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 11)) === true) + + // Return reward during the tenth epoch range + return new BigNumber(25000000); + + // Otherwise check if height is less than the eleventh block offset plus the eleventh epoch duration + else if(height.isLessThan(Consensus.getEpochBlockOffset(isMainnet, 11).plus(Consensus.getEpochDuration(isMainnet, 11))) === true) + + // Return reward during the eleventh epoch range + return new BigNumber(10000000); + + // Otherwise check if height is equal to the eleventh block offset plus the eleventh epoch duration + else if(height.isEqualTo(Consensus.getEpochBlockOffset(isMainnet, 11).plus(Consensus.getEpochDuration(isMainnet, 11))) === true) + + // Return reward during the twelfth epoch range + return new BigNumber(2211980); + + // Otherwise + else + + // Return zero + return new BigNumber(0); + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return block reward + return new BigNumber("60000000000"); + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check if is mainnet + if(isMainnet === true) { + + // Check if height is the first block era + if(height.isLessThanOrEqualTo(Consensus.FIRST_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the first block era + return new BigNumber("1600000000"); + } + + // Otherwise check if height is the second block era + else if(height.isLessThanOrEqualTo(Consensus.SECOND_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the second block era + return new BigNumber("800000000"); + } + + // Otherwise check if height is the third block era + else if(height.isLessThanOrEqualTo(Consensus.THIRD_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the third block era + return new BigNumber("400000000"); + } + + // Otherwise check if height is the fourth block era + else if(height.isLessThanOrEqualTo(Consensus.FOURTH_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the fourth block era + return new BigNumber("200000000"); + } + + // Otherwise check if height is the fifth block era + else if(height.isLessThanOrEqualTo(Consensus.FIFTH_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the fifth block era + return new BigNumber("100000000"); + } + + // Otherwise + else { + + // Get height after fifth era + var heightAfterFifthEra = height.minus(Consensus.FIFTH_BLOCK_ERA_HEIGHT + 1); + + // Return reward during current era + return (new BigNumber("15625000")).dividedToIntegerBy((new BigNumber(2)).exponentiatedBy(heightAfterFifthEra.dividedToIntegerBy(Consensus.SIXTH_AND_UP_BLOCK_ERA_INTERVAL))); + } + } + + // Otherwise + else { + + // Check if height is the first block era + if(height.isLessThanOrEqualTo(Consensus.FIRST_BLOCK_ERA_HEIGHT) === true) { + + // Return reward during the first block era + return new BigNumber("1600000000"); + } + + // Otherwise + else { + + // Return reward during the second block era + return new BigNumber("800000000"); + } + } + + // Break + break; + } + } + + // Get epoch duration + static getEpochDuration(isMainnet, epoch) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check epoch + switch(epoch) { + + // One + case 1: + + // Return duration of first epoch + return 0; + + // Two + case 2: + + // Return duration of second epoch + return (isMainnet === true) ? 120 * Consensus.BLOCK_HEIGHT_DAY : Consensus.BLOCK_HEIGHT_DAY; + + // Three + case 3: + + // Return duration of third epoch + return (isMainnet === true) ? 60 * Consensus.BLOCK_HEIGHT_DAY : Consensus.BLOCK_HEIGHT_DAY; + + // Four + case 4: + + // Return duration of fourth epoch + return 120 * Consensus.BLOCK_HEIGHT_DAY; + + // Five + case 5: + + // Return duration of fifth epoch + return 180 * Consensus.BLOCK_HEIGHT_DAY; + + // Six + case 6: + + // Return duration of sixth epoch + return 180 * Consensus.BLOCK_HEIGHT_DAY; + + // Seven + case 7: + + // Return duration of seventh epoch + return Consensus.BLOCK_HEIGHT_YEAR; + + // Eight + case 8: + + // Return duration of eighth epoch + return Consensus.BLOCK_HEIGHT_YEAR; + + // Nine + case 9: + + // Return duration of ninth epoch + return 6 * Consensus.BLOCK_HEIGHT_YEAR; + + // Tenth + case 10: + + // Return duration of tenth epoch + return 10 * Consensus.BLOCK_HEIGHT_YEAR; + + // Eleven + case 11: + + // Return duration of eleventh epoch + return 876349148; + } + } + } + + // Get epoch block offset + static getEpochBlockOffset(isMainnet, epoch) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Set offset to the C31 hard fork height + var offset = Consensus.getC31HardForkHeight(isMainnet); + + // Check if past second epoch range + if(epoch >= 2) + + // Add initial block duration to offset + offset = offset.plus((isMainnet === true) ? Consensus.BLOCK_HEIGHT_WEEK : Consensus.BLOCK_HEIGHT_DAY); + + // Go through all remaining epoch ranges + for(var i = 3; i <= epoch; ++i) + + // Add epoch's block duration to offset + offset = offset.plus(Consensus.getEpochDuration(isMainnet, i - 1)); + + // Return offset + return offset; + } + } + + // Get C31 hard fork height + static getC31HardForkHeight(isMainnet) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Return C31 hard fork height + return (isMainnet === true) ? new BigNumber(202500) : new BigNumber(270000); + } + } + + // Get header version + static getHeaderVersion(isMainnet, height) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if height is less than the C31 hard fork height + if(height.isLessThan(Consensus.getC31HardForkHeight(isMainnet)) === true) + + // Return legacy header version + return Consensus.LEGACY_HEADER_VERSION; + + // Otherwise + else + + // Return second header version + return Consensus.LEGACY_HEADER_VERSION.plus(1); + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check if mainnet + if(isMainnet === true) { + + // Get header version based on number of hard forks + var headerVersion = height.dividedToIntegerBy(Consensus.HARD_FORK_INTERVAL).plus(1); + + // Check if header version is greater than the maximum header version + if(headerVersion.isGreaterThan(Consensus.MAXIMUM_HEADER_VERSION) === true) + + // Set header version to the maximum header version + headerVersion = new BigNumber(Consensus.MAXIMUM_HEADER_VERSION); + + // Return header version + return headerVersion; + } + + // Otherwise + else { + + // Check is height is less than first hard fork height + if(height.isLessThan(Consensus.FIRST_HARD_FORK_HEIGHT) === true) + + // Return legacy header version + return Consensus.LEGACY_HEADER_VERSION; + + // Otherwise check if height is less than second hard fork height + else if(height.isLessThan(Consensus.SECOND_HARD_FORK_HEIGHT) === true) + + // Return second header version + return Consensus.LEGACY_HEADER_VERSION.plus(1); + + // Otherwise check if height is less than third hard fork height + else if(height.isLessThan(Consensus.THIRD_HARD_FORK_HEIGHT) === true) + + // Return third header version + return Consensus.LEGACY_HEADER_VERSION.plus(2); + + // Otherwise check if height is less than fourth hard forks height + else if(height.isLessThan(Consensus.FOURTH_HARD_FORK_HEIGHT) === true) + + // Return fourth header version + return Consensus.LEGACY_HEADER_VERSION.plus(3); + + // Otherwise + else + + // Return fifth header version + return Consensus.LEGACY_HEADER_VERSION.plus(4); + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check if height is less than the first hard fork height + if(height.isLessThan(Consensus.getFirstHardForkHeight(isMainnet)) === true) + + // Return legacy header version + return Consensus.LEGACY_HEADER_VERSION; + + // Otherwise + else + + // Return second header version + return Consensus.LEGACY_HEADER_VERSION.plus(1); + + // Break + break; + } + } + + // Get first hard fork height + static getFirstHardForkHeight(isMainnet) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return first hard fork height + return (isMainnet === true) ? new BigNumber(9000000) : new BigNumber(25800); + } + } + + // Hard fork interval + static get HARD_FORK_INTERVAL() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return hard fork interval + return Math.floor(Consensus.BLOCK_HEIGHT_YEAR / 2); + } + } + + // First hard fork height + static get FIRST_HARD_FORK_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return first hard fork height + return 185040; + } + } + + // Second hard fork height + static get SECOND_HARD_FORK_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return second hard fork height + return 298080; + } + } + + // Third hard fork height + static get THIRD_HARD_FORK_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return third hard fork height + return 552960; + } + } + + // Fourth hard fork height + static get FOURTH_HARD_FORK_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return fourth hard fork height + return 642240; + } + } + + // Maximum header version + static get MAXIMUM_HEADER_VERSION() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Return maximum header version + return 5; + } + } + + // Default Wallet type + static get DEFAULT_WALLET_TYPE() { + + // Return default wallet type + return Consensus.MWC_WALLET_TYPE; + } + + // Default network type + static get DEFAULT_NETWORK_TYPE() { + + // Return default network type + return Consensus.MAINNET_NETWORK_TYPE; + } + + // Wallet MWC text value + static get WALLET_MWC_TEXT_VALUE() { + + // Return wallet MWC text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('MimbleWimble Coin') : "MimbleWimble Coin"; + } + + // Wallet GRIN value + static get WALLET_GRIN_TEXT_VALUE() { + + // Return wallet GRIN text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('Grin') : "Grin"; + } + + // Wallet EPIC value + static get WALLET_EPIC_TEXT_VALUE() { + + // Return wallet EPIC text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('Epic Cash') : "Epic Cash"; + } + + // Network mainnet value + static get NETWORK_MAINNET_TEXT_VALUE() { + + // Return network mainnet text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('Mainnet') : "Mainnet"; + } + + // Network floonet value + static get NETWORK_FLOONET_TEXT_VALUE() { + + // Return network floonet text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('Floonet') : "Floonet"; + } + + // Network testnet value + static get NETWORK_TESTNET_TEXT_VALUE() { + + // Return network testnet text value + return (typeof Language !== "undefined") ? Language.getDefaultTranslation('Testnet') : "Testnet"; + } + + // Override wallet type URL parameter name + static get OVERRIDE_WALLET_TYPE_URL_PARAMETER_NAME() { + + // Return override wallet type URL parameter name + return "Override Wallet Type"; + } + + // Override network type URL parameter name + static get OVERRIDE_NETWORK_TYPE_URL_PARAMETER_NAME() { + + // Return override network type URL parameter name + return "Override Network Type"; + } + + // First block era height + static get FIRST_BLOCK_ERA_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return first block era height + return 334 * Consensus.BLOCK_HEIGHT_DAY; + } + } + + // Second block era height + static get SECOND_BLOCK_ERA_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return second block era height + return 470 * Consensus.BLOCK_HEIGHT_DAY + Consensus.FIRST_BLOCK_ERA_HEIGHT; + } + } + + // Third block era height + static get THIRD_BLOCK_ERA_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return third block era height + return 601 * Consensus.BLOCK_HEIGHT_DAY + Consensus.SECOND_BLOCK_ERA_HEIGHT; + } + } + + // Fourth block era height + static get FOURTH_BLOCK_ERA_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return fourth block era height + return 800 * Consensus.BLOCK_HEIGHT_DAY + Consensus.THIRD_BLOCK_ERA_HEIGHT; + } + } + + // Fifth block era height + static get FIFTH_BLOCK_ERA_HEIGHT() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return fifth block era height + return 1019 * Consensus.BLOCK_HEIGHT_DAY + Consensus.FOURTH_BLOCK_ERA_HEIGHT; + } + } + + // Sixth and up block era interval + static get SIXTH_AND_UP_BLOCK_ERA_INTERVAL() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Return sixth and up block era interval + return 1460 * Consensus.BLOCK_HEIGHT_DAY; + } + } + + // Wallet type local storage name + static get WALLET_TYPE_LOCAL_STORAGE_NAME() { + + // Return wallet type local storage name + return "Wallet Type"; + } + + // Network type local storage name + static get NETWORK_TYPE_LOCAL_STORAGE_NAME() { + + // Return network type local storage name + return "Network Type"; + } +} + + +// Main function + +// Set global object's consensus +globalThis["Consensus"] = Consensus; + +// Check if not not a web worker +if(typeof self === "undefined" || typeof WorkerGlobalScope === "undefined" || self instanceof WorkerGlobalScope === false) { + + // Initialize consensus + Consensus.initialize(); +} diff --git a/scripts/cookie_acceptance.js b/scripts/cookie_acceptance.js new file mode 100755 index 0000000..6267bc4 --- /dev/null +++ b/scripts/cookie_acceptance.js @@ -0,0 +1,180 @@ +// Use strict +"use strict"; + + +// Classes + +// Cookie acceptance class +class CookieAcceptance { + + // Public + + // Constructor + constructor() { + + // Get cookie acceptance display + this.cookieAcceptanceDisplay = $("section.cookieAcceptance"); + + // Get acknowledge button + this.acknowledgeButton = this.cookieAcceptanceDisplay.find("button"); + + // Set can show + this.canShow = false; + + // Set self + var self = this; + + // Cookie acceptance display transaition end event + this.cookieAcceptanceDisplay.on("transitionend", function() { + + // Check if cookie acceptance display is hiding + if(self.cookieAcceptanceDisplay.hasClass("hide") === true) { + + // Prevent focus on cookie acceptance display's elements + self.cookieAcceptanceDisplay.addClass("noFocus"); + + // Trigger is hidden event + $(self).trigger(CookieAcceptance.IS_HIDDEN_EVENT); + } + }); + + // Window storage event + $(window).on("storage", function(event) { + + // Check if cookie acceptance message acknowledged was changed + if(event["originalEvent"]["key"] === CookieAcceptance.COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_LOCAL_STORAGE_NAME) { + + // Hide + self.hide(); + + // Clear can show + self.canShow = false; + } + }); + + // Acknowledged button click event + this.acknowledgeButton.on("click", function() { + + // Hide + self.hide(); + + // Clear can show + self.canShow = false; + + // Try + try { + + // Save cookie acceptance message acknowledged + localStorage.setItem(CookieAcceptance.COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_LOCAL_STORAGE_NAME, CookieAcceptance.COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_TRUE_VALUE); + } + + // Catch errors + catch(error) { + + // Trigger a fatal error + new FatalError(FatalError.LOCAL_STORAGE_ERROR); + } + }); + } + + // Show + show(canShow = true) { + + // Check if can show + if(canShow === true || this.canShow === true) { + + // Set can show + this.canShow = true; + + // Check if not an app or extension and not loading from file + if(Common.isApp() === false && Common.isExtension() === false && location["protocol"] !== Common.FILE_PROTOCOL) { + + // Get cookie acceptance message acknowledged + var cookieAcceptanceMessageAcknowledged = localStorage.getItem(CookieAcceptance.COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_LOCAL_STORAGE_NAME); + + // Check if cookie acceptance message hasn't been acknowledged + if(cookieAcceptanceMessageAcknowledged === Common.INVALID_LOCAL_STORAGE_ITEM || cookieAcceptanceMessageAcknowledged !== CookieAcceptance.COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_TRUE_VALUE) { + + // Show cookie acceptance display and make it so that its elements can be focused + this.cookieAcceptanceDisplay.removeClass("hide noFocus"); + + // Set self + var self = this; + + // Windows resize cookie acceptance event + $(window).on("resize.cookieAcceptance", function() { + + // Check if is an app + if(Common.isApp() === true) { + + // Turn off window resize cookie acceptance event + $(window).off("resize.cookieAcceptance"); + + // Hide + self.hide(); + + // Clear can show + self.canShow = false; + } + }); + + // Return true + return true; + } + + // Otherwise + else { + + // Trigger is hidden event + $(this).trigger(CookieAcceptance.IS_HIDDEN_EVENT); + } + } + + // Otherwise + else { + + // Trigger is hidden event + $(this).trigger(CookieAcceptance.IS_HIDDEN_EVENT); + } + } + + // Return false + return false; + } + + // Is hidden event + static get IS_HIDDEN_EVENT() { + + // Return is hidden event + return "CookieAcceptanceIsHiddenEvent"; + } + + // Private + + // Hide + hide() { + + // Hide cookie acceptance display + this.cookieAcceptanceDisplay.addClass("hide"); + } + + // Cookie acceptance message acknowledged local storage name + static get COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_LOCAL_STORAGE_NAME() { + + // Return cookie acceptance message acknowledged local storage name + return "Cookie Acceptance Message Acknowledged"; + } + + // Cookie acceptance message acknowledged true value + static get COOKIE_ACCEPTANCE_MESSAGE_ACKNOWLEDGED_TRUE_VALUE() { + + // Return cookie acceptance message acknowledged true value + return "true"; + } +} + + +// Main function + +// Set global object's cookie acceptance +globalThis["CookieAcceptance"] = CookieAcceptance; diff --git a/scripts/copyright.js b/scripts/copyright.js new file mode 100755 index 0000000..bdeffbf --- /dev/null +++ b/scripts/copyright.js @@ -0,0 +1,100 @@ +// Use strict +"use strict"; + + +// Classes + +// Copyright class +class Copyright { + + // Public + + // Initialize + static initialize() { + + // Update + Copyright.update(); + } + + // Private + + // Update + static update() { + + // Get current timestamp + var currentTimestamp = new Date(); + + // Get current year + var currentYear = currentTimestamp.getFullYear(); + + // Check if the current year is greater than the copyright year + if(currentYear > COPYRIGHT_YEAR) { + + // Get new date copyright + var newDateCopyright = $(Language.createTranslatableContainer("", Language.getDefaultTranslation('%1$s–%2$s'), [COPYRIGHT_YEAR.toFixed(), currentYear.toFixed()])); + + // Get new rights + var newRights = $(Language.createTranslatableContainer("", Language.getDefaultTranslation('© %1$s–%2$s Nicolas Flamel.'), [COPYRIGHT_YEAR.toFixed(), currentYear.toFixed()])); + } + + // Otherwise + else { + + // Get new date copyright + var newDateCopyright = $(Language.createTranslatableContainer("", "%1$s", [COPYRIGHT_YEAR.toFixed()])); + + // Get new rights + var newRights = $(Language.createTranslatableContainer("", Language.getDefaultTranslation('© %1$s Nicolas Flamel.'), [COPYRIGHT_YEAR.toFixed()])); + } + + // Set new date copyright's name + newDateCopyright.attr("name", Copyright.DATE_COPYRIGHT_NAME); + + // Set new rights's name + newRights.attr("name", Copyright.RIGHTS_NAME); + + // Replace date copyright with the new date copyright + $("meta[name=\"" + Copyright.DATE_COPYRIGHT_NAME + "\"]").replaceWith(newDateCopyright); + + // Replace rights with the new rights + $("meta[name=\"" + Copyright.RIGHTS_NAME + "\"]").replaceWith(newRights); + + // Get next year timestamp + var nextYearTimestamp = new Date(currentYear + 1, Common.JANUARY_MONTH_INDEX); + + // Set timeout + setTimeout(function() { + + // Update + Copyright.update(); + + }, Math.min(nextYearTimestamp - currentTimestamp, Common.INT32_MAX_VALUE)); + } + + // Date copyright name + static get DATE_COPYRIGHT_NAME() { + + // Return date copyright name + return "dcterms.dateCopyrighted"; + } + + // Rights name + static get RIGHTS_NAME() { + + // Return rights name + return "dcterms.rights"; + } +} + + +// Main function + +// Set global object's copyright +globalThis["Copyright"] = Copyright; + +// Ready event +$(function() { + + // Initialize copyright + Copyright.initialize(); +}); diff --git a/scripts/crc32 license.txt b/scripts/crc32 license.txt new file mode 100755 index 0000000..9fd9d56 --- /dev/null +++ b/scripts/crc32 license.txt @@ -0,0 +1,14 @@ +Copyright (C) 2014-present SheetJS + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/scripts/crc32-1.2.0.js b/scripts/crc32-1.2.0.js new file mode 100755 index 0000000..6bcecef --- /dev/null +++ b/scripts/crc32-1.2.0.js @@ -0,0 +1,118 @@ +/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + if(typeof DO_NOT_EXPORT_CRC === 'undefined') { + if('object' === typeof exports) { + factory(exports); + } else if ('function' === typeof define && define.amd) { + define(function () { + var module = {}; + factory(module); + return module; + }); + } else { + factory(CRC32 = {}); + } + } else { + factory(CRC32 = {}); + } + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.0'; +/* see perf/crc32table.js */ +/*global Int32Array */ +function signed_crc_table() { + var c = 0, table = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr, seed) { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf, seed) { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf, seed) { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str, seed) { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +// $FlowIgnore +CRC32.bstr = crc32_bstr; +// $FlowIgnore +CRC32.buf = crc32_buf; +// $FlowIgnore +CRC32.str = crc32_str; +})); diff --git a/scripts/crypto.js b/scripts/crypto.js new file mode 100755 index 0000000..647b002 --- /dev/null +++ b/scripts/crypto.js @@ -0,0 +1,1194 @@ +// Use strict +"use strict"; + + +// Classes + +// Crypto class +class Crypto { + + // Public + + // Derive child key + static deriveChildKey(extendedPrivateKey, paths, useBip39 = false) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Get secret key and chain code from extended private key + var secretKey = extendedPrivateKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH); + var chainCode = extendedPrivateKey.subarray(Crypto.CHAIN_CODE_LENGTH); + + // Set update values + var updateValues = new Promise(function(resolve, reject) { + + // Resolve secret key and chain code + resolve([ + + // Copy secret key + new Uint8Array(secretKey), + + // Copy chain code + new Uint8Array(chainCode) + ]); + }); + + // Initialize updating values + var updatingValues = [updateValues]; + + // Go through all of the paths + for(let i = 0; i < paths["length"]; ++i) { + + // Set update to run after the previous update + updateValues = updateValues.then(function(values) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Get updated secret key and chain code + var updatedSecretKey = values[Crypto.SECRET_KEY_INDEX]; + var updatedChainCode = values[Crypto.CHAIN_CODE_INDEX]; + + // Return deriving the next secret key and chain code using the path + return Crypto.deriveSecretKeyAndChainCode(updatedSecretKey, updatedChainCode, paths[i], useBip39).then(function(updatedValues) { + + // Securely clear updated secret key and chain code + updatedSecretKey.fill(0); + updatedChainCode.fill(0); + + // Resolve updated values + resolve(updatedValues); + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key and chain code + updatedSecretKey.fill(0); + updatedChainCode.fill(0); + + // Reject error + reject(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Return Promise + return new Promise(function(resolve, reject) { + + // Reject error + reject(error); + }); + }); + + // Append updating value to list + updatingValues.push(updateValues); + } + + // Return updating all values + return Promise.all(updatingValues).then(function(values) { + + // Get updated secret key and chain code + var updatedSecretKey = values[values["length"] - 1][Crypto.SECRET_KEY_INDEX]; + var updatedChainCode = values[values["length"] - 1][Crypto.CHAIN_CODE_INDEX]; + + // Initialize new extended private key + var newExtendedPrivateKey = Common.mergeArrays([ + + // Updated secret key + updatedSecretKey, + + // Chain code + updatedChainCode + ]); + + // Securely clear updated secret key and chain code + updatedSecretKey.fill(0); + updatedChainCode.fill(0); + + // Check if new extended private key's secret key is a valid secret key + if(Secp256k1Zkp.isValidSecretKey(newExtendedPrivateKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH)) === true) { + + // Resolve new extended private key + resolve(newExtendedPrivateKey); + } + + // Otherwise + else { + + // Securely clear new extended private key + newExtendedPrivateKey.fill(0); + + // Reject error + reject("Invalid extended private key."); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Commit + static commit(extendedPrivateKey, amount, identifier, switchType) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return deriving secret key from extended private key, amount, identifier, and switch type + return Crypto.deriveSecretKey(extendedPrivateKey, amount, identifier, switchType).then(function(secretKey) { + + // Check if performing Pedersen commit with the secret key and amount was successful + var commit = Secp256k1Zkp.pedersenCommit(secretKey, amount.toFixed()); + + if(commit !== Secp256k1Zkp.OPERATION_FAILED) { + + // Resolve commit + resolve(commit); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject("Performing Pedersen commit failed."); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Commit amount + static commitAmount(amount) { + + // Check if performing Pedersen commit with zero blinding factor and amount was successful + var commit = Secp256k1Zkp.pedersenCommit(Crypto.ZERO_BLINDING_FACTOR, amount.toFixed()); + + if(commit !== Secp256k1Zkp.OPERATION_FAILED) { + + // Return commit + return commit; + } + + // Otherwise + else { + + // Throw error + throw "Performing Pedersen commit failed."; + } + } + + // Proof + static proof(extendedPrivateKey, amount, identifier, switchType, proofBuilder) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return getting commit from extended private key, amount, identifier, and switch type + return Crypto.commit(extendedPrivateKey, amount, identifier, switchType).then(function(commit) { + + // Return deriving secret key from extended private key, amount, identifier, and switch type + return Crypto.deriveSecretKey(extendedPrivateKey, amount, identifier, switchType).then(function(secretKey) { + + // Return getting rewind nonce from the commit + return proofBuilder.rewindNonce(commit).then(function(rewindNonce) { + + // Return getting private nonce from the commit + return proofBuilder.privateNonce(commit).then(function(privateNonce) { + + // Try + try { + + // Get proof message from identifier and switch type + var message = proofBuilder.proofMessage(identifier, switchType); + } + + // Catch errors + catch(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Securely clear private nonce + privateNonce.fill(0); + + // Reject error + reject("Getting proof message failed."); + + // Return + return; + } + + // Check if creating bulletproof with the secret key, amount, rewind nonce, private nonce, and message was successful + var bulletproof = Secp256k1Zkp.createBulletproof(secretKey, amount.toFixed(), rewindNonce, privateNonce, new Uint8Array([]), message); + + if(bulletproof !== Secp256k1Zkp.OPERATION_FAILED) { + + // Securely clear secret key + secretKey.fill(0); + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Securely clear private nonce + privateNonce.fill(0); + + // Resolve bulletproof + resolve(bulletproof); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Securely clear private nonce + privateNonce.fill(0); + + // Reject error + reject("Getting bulletproof failed."); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Derive secret key + static deriveSecretKey(extendedPrivateKey, amount, identifier, switchType) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Initialize paths + var paths = new Uint32Array(identifier.getDepth()); + + // Go through all the identifier's paths + for(var i = 0; i < identifier.getDepth(); ++i) { + + // Set path to identifier's path + paths[i] = identifier.getPaths()[i]; + } + + // Return deriving the child key at the path from the extended private key + return Crypto.deriveChildKey(extendedPrivateKey, paths).then(function(childKey) { + + // Get secret key from the child key + var secretKey = new Uint8Array(childKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH)); + + // Securely clear child key + childKey.fill(0); + + // Check if switch type is none + if(switchType === Crypto.SWITCH_TYPE_NONE) + + // Resolve secret key + resolve(secretKey); + + // Otherwise check if switch type is regular + else if(switchType === Crypto.SWITCH_TYPE_REGULAR) { + + // Check if getting blind switch of secret key and amount failed + var blindSwitch = Secp256k1Zkp.blindSwitch(secretKey, amount.toFixed()); + + if(blindSwitch === Secp256k1Zkp.OPERATION_FAILED) { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject("Performing blind switch failed."); + + // Return + return; + } + + // Securely clear secret key + secretKey.fill(0); + + // Check if blind switch is a valid secret key + if(Secp256k1Zkp.isValidSecretKey(blindSwitch) === true) { + + // Resolve blind switch + resolve(blindSwitch); + } + + // Otherwise + else { + + // Securely clear blind switch + blindSwitch.fill(0); + + // Reject error + reject("Blind switch isn't a valid secret key."); + } + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject("Invalid switch type."); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Signature message + static signatureMessage(data) { + + // Check if creating message was successful + var message = Blake2b.compute(Crypto.SINGLE_SIGNER_MESSAGE_LENGTH, data, new Uint8Array([])); + + if(message !== Blake2b.OPERATION_FAILED) { + + // Return message + return message; + } + + // Otherwise + else { + + // Throw error + throw "Creating signature message failed."; + } + } + + // Root public key + static rootPublicKey(extendedPrivateKey) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Get secret key from extended private key + var secretKey = extendedPrivateKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH); + + // Check if getting public key from secret key was successful + var publicKey = Secp256k1Zkp.publicKeyFromSecretKey(secretKey); + + if(publicKey !== Secp256k1Zkp.OPERATION_FAILED) { + + // Resolve public key + resolve(publicKey); + } + + // Otherwise + else { + + // Reject error + reject("Getting public key failed."); + } + }); + } + + // Address key + static addressKey(extendedPrivateKey, index) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Return deriving root key from extended private key, address key amount, root identifier, and regular switch type + return Crypto.deriveSecretKey(extendedPrivateKey, Crypto.ADDRESS_KEY_AMOUNT, new Identifier(Identifier.ROOT_SERIALIZED_IDENTIFIER), Crypto.SWITCH_TYPE_REGULAR).then(function(rootKey) { + + // Return creating crypto key from address key seed + return crypto["subtle"].importKey("raw", (new TextEncoder()).encode(Crypto.ADDRESS_KEY_SEED), { + + // Name + "name": Crypto.ADDRESS_KEY_ENCRYPTION_ALGORITHM, + + // Hash + "hash": { + + // Name + "name": Crypto.ADDRESS_KEY_DIGEST_ALGORITHM + } + }, false, [ + + // Sign + "sign" + + ]).then(function(cryptoKey) { + + // Return creating address extended private key from signing the root key + return crypto["subtle"].sign(Crypto.ADDRESS_KEY_ENCRYPTION_ALGORITHM, cryptoKey, rootKey).then(function(addressExtendedPrivateKey) { + + // Securely clear root key + rootKey.fill(0); + + // Get address extended private key in correct format + addressExtendedPrivateKey = new Uint8Array(addressExtendedPrivateKey); + + // Get secret key and chain code from address extended private key + var secretKey = addressExtendedPrivateKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH); + var chainCode = addressExtendedPrivateKey.subarray(Crypto.CHAIN_CODE_LENGTH); + + // Check if secret key is a valid secret key + if(Secp256k1Zkp.isValidSecretKey(secretKey) === true) { + + // Return updating the next secret key and chain code using the index + return Crypto.deriveSecretKeyAndChainCode(secretKey, chainCode, index).then(function(value) { + + // Securely clear address extended private key + addressExtendedPrivateKey.fill(0); + + // Securely clear the chain code + value[Crypto.CHAIN_CODE_INDEX].fill(0); + + // Resolve the secret key + resolve(value[Crypto.SECRET_KEY_INDEX]); + + // Catch errors + }).catch(function(error) { + + // Securely clear address extended private key + addressExtendedPrivateKey.fill(0); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Securely clear address extended private key + addressExtendedPrivateKey.fill(0); + + // Reject error + reject("Secret key is not a valid secret key."); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear root key + rootKey.fill(0); + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Securely clear root key + rootKey.fill(0); + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + + // GRIN or EPIC wallet + case Consensus.GRIN_WALLET_TYPE: + case Consensus.EPIC_WALLET_TYPE: + + // Create child identifier + var childIdentifier = new Identifier().getChild(); + childIdentifier.getPaths()[1] = 1; + childIdentifier.getPaths()[childIdentifier.getDepth() - 1] = index; + + // Return deriving root key from extended private key, child identifier, and no switch type + return Crypto.deriveSecretKey(extendedPrivateKey, new BigNumber(0), childIdentifier, Crypto.SWITCH_TYPE_NONE).then(function(rootKey) { + + // Check if getting secret key from root key was successful + var secretKey = Blake2b.compute(Crypto.SECP256K1_SECRET_KEY_LENGTH, rootKey, new Uint8Array([])); + + if(secretKey !== Blake2b.OPERATION_FAILED) { + + // Securely clear root key + rootKey.fill(0); + + // Check if secret key is a valid secret key + if(Secp256k1Zkp.isValidSecretKey(secretKey) === true) { + + // Resolve the secret key + resolve(secretKey); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject("Secret key is not a valid secret key."); + } + } + + // Otherwise + else { + + // Securely clear root key + rootKey.fill(0); + + // Reject error + reject("Getting secret key from root key failed."); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + }); + } + + // Login key + static loginKey(extendedPrivateKey) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Create child identifier + var childIdentifier = new Identifier().getChild(); + childIdentifier.getPaths()[1] = 2; + + // Return deriving root key from extended private key, child identifier, and no switch type + return Crypto.deriveSecretKey(extendedPrivateKey, new BigNumber(0), childIdentifier, Crypto.SWITCH_TYPE_NONE).then(function(rootKey) { + + // Check if getting secret key from root key was successful + var secretKey = Blake2b.compute(Crypto.SECP256K1_SECRET_KEY_LENGTH, rootKey, new Uint8Array([])); + + if(secretKey !== Blake2b.OPERATION_FAILED) { + + // Securely clear root key + rootKey.fill(0); + + // Check if secret key is a valid secret key + if(Secp256k1Zkp.isValidSecretKey(secretKey) === true) { + + // Resolve the secret key + resolve(secretKey); + } + + // Otherwise + else { + + // Securely clear secret key + secretKey.fill(0); + + // Reject error + reject("Secret key is not a valid secret key."); + } + } + + // Otherwise + else { + + // Securely clear root key + rootKey.fill(0); + + // Reject error + reject("Getting secret key from root key failed."); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // AES decrypt + static aesDecrypt(encryptedData, key) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return importing the key + return crypto["subtle"].importKey("raw", key, { + + // Name + "name": Crypto.AES_ALGORITHM + + }, false, [ + + // Decrypt + "decrypt" + + ]).then(function(cryptoKey) { + + // Return decrypting the encrypted data using the AES key + return crypto["subtle"].decrypt({ + + // Name + "name": Crypto.AES_ALGORITHM, + + // Initialization vector + "iv": (new Uint8Array(Crypto.AES_INITIALIZATION_VECTOR_SIZE)).fill(0) + + }, cryptoKey, encryptedData).then(function(decryptedData) { + + // TODO Securely clear cryptoKey + + // Resolve decrypted data + resolve(new Uint8Array(decryptedData)); + + // Catch errors + }).catch(function(error) { + + // TODO Securely clear cryptoKey + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Switch type none + static get SWITCH_TYPE_NONE() { + + // Return switch type none + return 0; + } + + // Switch type regular + static get SWITCH_TYPE_REGULAR() { + + // Return switch type regular + return Crypto.SWITCH_TYPE_NONE + 1; + } + + // Secp256k1 secret key length + static get SECP256K1_SECRET_KEY_LENGTH() { + + // Return secp256k1 secret key length + return 32; + } + + // Secp256k1 public key length + static get SECP256K1_PUBLIC_KEY_LENGTH() { + + // Return secp256k1 public key length + return 33; + } + + // Blinding factor length + static get BLINDING_FACTOR_LENGTH() { + + // Return blinding factor length + return 32; + } + + // Commit length + static get COMMIT_LENGTH() { + + // Return commit length + return 33; + } + + // Single-signer signature length + static get SINGLE_SIGNER_SIGNATURE_LENGTH() { + + // Return single-signer signature length + return 64; + } + + // Single-signer message length + static get SINGLE_SIGNER_MESSAGE_LENGTH() { + + // Return single-signer message length + return 32; + } + + // Proof length + static get PROOF_LENGTH() { + + // Return proof length + return 675; + } + + // Zero blinding factor + static get ZERO_BLINDING_FACTOR() { + + // No zero blinding factor + return (new Uint8Array(Crypto.BLINDING_FACTOR_LENGTH)).fill(0); + } + + // Zero secret key + static get ZERO_SECRET_KEY() { + + // No zero blinding factor + return (new Uint8Array(Crypto.SECP256K1_SECRET_KEY_LENGTH)).fill(0); + } + + // Ed25519 secret key length + static get ED25519_SECRET_KEY_LENGTH() { + + // Return Ed25519 secret key length + return 32; + } + + // Ed25519 public key length + static get ED25519_PUBLIC_KEY_LENGTH() { + + // Return Ed25519 public key length + return 32; + } + + // X25519 secret key length + static get X25519_SECRET_KEY_LENGTH() { + + // Return X25519 secret key length + return 32; + } + + // X25519 public key length + static get X25519_PUBLIC_KEY_LENGTH() { + + // Return X25519 public key length + return 32; + } + + // Ed25519 signature length + static get ED25519_SIGNATURE_LENGTH() { + + // Return Ed25519 signature length + return 64; + } + + // Maximum message hash signature length + static get MAXIMUM_MESSAGE_HASH_SIGNATURE_LENGTH() { + + // Return maximum message hash signature length + return 72; + } + + // Nonce length + static get NONCE_LENGTH() { + + // Return nonce length + return 32; + } + + // Tau x length + static get TAU_X_LENGTH() { + + // Return tau x length + return 32; + } + + // AES key length + static get AES_KEY_LENGTH() { + + // Return AES key length + return 32; + } + + // Private + + // Derive secret key and chain code + static deriveSecretKeyAndChainCode(secretKey, chainCode, path, useBip39 = false) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return creating crypto key from chain code + return crypto["subtle"].importKey("raw", chainCode, { + + // Name + "name": Crypto.CHAIN_CODE_ENCRYPTION_ALGORITHM, + + // Hash + "hash": { + + // Name + "name": Crypto.CHAIN_CODE_DIGEST_ALGORITHM + } + }, false, [ + + // Sign + "sign" + + ]).then(function(cryptoKey) { + + // Check if path is hardened + if(Identifier.isPathHardened(path) === true) { + + // Get hash value from secret key + var hashValue = Common.mergeArrays([ + + // Zero + new Uint8Array([0]), + + // Secret key + secretKey + ]); + } + + // Otherwise + else { + + // Check if getting hash value as public key from secret key failed + var hashValue = Secp256k1Zkp.publicKeyFromSecretKey(secretKey); + + if(hashValue === Secp256k1Zkp.OPERATION_FAILED) { + + // TODO Securely clear cryptoKey + + // Reject error + reject("Getting public key from secret key failed."); + + // Return + return; + } + } + + // Create secret key and chain code + var createSecretKeyAndChainCode = function(hashValueCandidate) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Append path to hash value candidate + var pathBuffer = new ArrayBuffer(Uint32Array["BYTES_PER_ELEMENT"]); + + var pathBufferView = new DataView(pathBuffer); + pathBufferView.setUint32(0, path, false); + + var newHashValue = Common.mergeArrays([ + + // Hash value candidate + hashValueCandidate, + + // Path + new Uint8Array(pathBuffer) + ]); + + // Securely clear hash value candidate + hashValueCandidate.fill(0); + + // Return creating new extended private key from signing the new hash value + return crypto["subtle"].sign(Crypto.CHAIN_CODE_ENCRYPTION_ALGORITHM, cryptoKey, newHashValue).then(function(extendedPrivateKey) { + + // Securely clear new hash value + newHashValue.fill(0); + + // Get extended private key in correct format + extendedPrivateKey = new Uint8Array(extendedPrivateKey); + + // Get new secret key from the extended private key + var newSecretKey = extendedPrivateKey.subarray(0, Crypto.SECP256K1_SECRET_KEY_LENGTH); + + // Check if new secret key is a valid secret key or using BIP39 and new secret key is zero + if(Secp256k1Zkp.isValidSecretKey(newSecretKey) === true || (useBip39 === true && Common.arraysAreEqualTimingSafe(newSecretKey, Crypto.ZERO_SECRET_KEY) === true)) { + + // Check if adding old secret key to the new secret key was successful + var updatedSecretKey = Secp256k1Zkp.secretKeyTweakAdd(newSecretKey, secretKey); + + if(updatedSecretKey !== Secp256k1Zkp.OPERATION_FAILED) { + + // Get updated chain code from the extended private key + var updatedChainCode = new Uint8Array(extendedPrivateKey.subarray(Crypto.SECP256K1_SECRET_KEY_LENGTH)); + + // Securely clear extended private key + extendedPrivateKey.fill(0); + + // Resolve new extended private key + resolve([ + + // Updated secret key + updatedSecretKey, + + // Updated chain code + updatedChainCode + ]); + } + + // Otherwise + else { + + // Check if using BIP39 + if(useBip39 === true) { + + // Set next hash value candidate + var nextHashValueCandidate = Common.mergeArrays([ + + // One + new Uint8Array([1]), + + // New chain code + extendedPrivateKey.subarray(Crypto.SECP256K1_SECRET_KEY_LENGTH) + ]); + + // Securely clear extended private key + extendedPrivateKey.fill(0); + + // Return creating secret key and chain code + return createSecretKeyAndChainCode(nextHashValueCandidate).then(function(secretKeyAndChainCode) { + + // Resolve secret key and chain code + resolve(secretKeyAndChainCode); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Securely clear extended private key + extendedPrivateKey.fill(0); + + // Reject error + reject("Performing secret key tweak add failed."); + } + } + } + + // Otherwise + else { + + // Check if using BIP39 + if(useBip39 === true) { + + // Set next hash value candidate + var nextHashValueCandidate = Common.mergeArrays([ + + // One + new Uint8Array([1]), + + // New chain code + extendedPrivateKey.subarray(Crypto.SECP256K1_SECRET_KEY_LENGTH) + ]); + + // Securely clear extended private key + extendedPrivateKey.fill(0); + + // Return creating secret key and chain code + return createSecretKeyAndChainCode(nextHashValueCandidate).then(function(secretKeyAndChainCode) { + + // Resolve secret key and chain code + resolve(secretKeyAndChainCode); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Securely clear extended private key + extendedPrivateKey.fill(0); + + // Reject error + reject("New secret key isn't a valid secret key or using BIP39 and new secret key isn't zero."); + } + } + + // Catch errors + }).catch(function(error) { + + // Securely clear new hash value + newHashValue.fill(0); + + // Reject error + reject("Creating new extended private key failed."); + }); + }); + }; + + // Return creating secret key and chain code + return createSecretKeyAndChainCode(hashValue).then(function(secretKeyAndChainCode) { + + // TODO Securely clear cryptoKey + + // Resolve secret key and chain code + resolve(secretKeyAndChainCode); + + // Catch errors + }).catch(function(error) { + + // TODO Securely clear cryptoKey + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject("Creating crypto key failed."); + }); + }); + } + + // Chain code length + static get CHAIN_CODE_LENGTH() { + + // Return chain code length + return 32; + } + + // Secret key index + static get SECRET_KEY_INDEX() { + + // Return secret key index + return 0; + } + + // Chain code index + static get CHAIN_CODE_INDEX() { + + // Return chain code index + return Crypto.SECRET_KEY_INDEX + 1; + } + + // Chain code encryption algorithm + static get CHAIN_CODE_ENCRYPTION_ALGORITHM() { + + // Return chain code encryption algorithm + return "HMAC"; + } + + // Chain code digest algorithm + static get CHAIN_CODE_DIGEST_ALGORITHM() { + + // Return chain code digest algorithm + return "SHA-512"; + } + + // Address key amount + static get ADDRESS_KEY_AMOUNT() { + + // Return address key amount + return new BigNumber(713); + } + + // Address key seed + static get ADDRESS_KEY_SEED() { + + // Return address key seed + return "Grinbox_seed"; + } + + // Address key encryption algorithm + static get ADDRESS_KEY_ENCRYPTION_ALGORITHM() { + + // Return address key encryption algorithm + return "HMAC"; + } + + // Address key digest algorithm + static get ADDRESS_KEY_DIGEST_ALGORITHM() { + + // Return address key digest algorithm + return "SHA-512"; + } + + // AES algorithm + static get AES_ALGORITHM() { + + // Return AES algorithm + return "AES-CBC"; + } + + // AES initialization vector size + static get AES_INITIALIZATION_VECTOR_SIZE() { + + // Return AES initialization vector size + return 16; + } +} + + +// Main function + +// Set global object's crypto +globalThis["Crypto"] = Crypto; diff --git a/scripts/database.js b/scripts/database.js new file mode 100755 index 0000000..da0b285 --- /dev/null +++ b/scripts/database.js @@ -0,0 +1,1758 @@ +// Use strict +"use strict"; + + +// Classes + +// Database class +class Database { + + // Public + + // Initialize + static initialize() { + + // Set instance to invalid + Database.instance = Database.INVALID; + + // Set object store locks + Database.objectStoreLocks = new Set(); + + // Set object store unlock event index + Database.objectStoreUnlockEventIndex = 0; + + // Return promise + return new Promise(function(resolve, reject) { + + // Try + try { + + // Request database + var databaseRequest = indexedDB.open(Database.NAME, Database.VERSION); + } + + // Catch errors + catch(error) { + + // Reject error + reject("Failed to create and/or open database."); + + // Return + return; + } + + // Database request on error + databaseRequest["onerror"] = function() { + + // Reject + reject(); + }; + + // Database request on upgrade needed + databaseRequest["onupgradeneeded"] = function(event) { + + // Get database + var database = event["target"]["result"]; + + // Get database transaction + var databaseTransaction = event["target"]["transaction"]; + + // Get current version + var currentVersion = event["oldVersion"]; + + // Database on error + database["onerror"] = function() { + + // Reject + reject(); + }; + + // Check if new databases exists + if(typeof Database.newDatabases !== "undefined") { + + // Go through all new databases + for(var i = 0; i < Database.newDatabases["length"]; ++i) + + // Create new database + Database.newDatabases[i](database, currentVersion, databaseTransaction); + } + }; + + // Database request on success + databaseRequest["onsuccess"] = function(event) { + + // Set instance + Database.instance = event["target"]["result"]; + + // Check if critical initialization functions don't exist + if(typeof Database.criticalInitializationFunctions === "undefined") + + // Create critical initialzation functions + Database.criticalInitializationFunctions = []; + + // Run all critical initialization functions + Promise.all(Database.criticalInitializationFunctions.map(function(criticalInitializationFunction) { + + // Return performing critical initialization function + return criticalInitializationFunction(); + + })).then(function() { + + // Check if initialization functions don't exist + if(typeof Database.initializationFunctions === "undefined") + + // Create initialization functions + Database.initializationFunctions = []; + + // Run all initialization functions + Promise.all(Database.initializationFunctions.map(function(initializationFunction) { + + // Return performing initialization function + return initializationFunction(); + + })).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }; + }); + } + + // Create database + static createDatabase(newDatabase) { + + // Check if new databases don't exist + if(typeof Database.newDatabases === "undefined") + + // Create new databases + Database.newDatabases = []; + + // Append new database to list + Database.newDatabases.push(newDatabase); + } + + // Once initialized + static onceInitialized(initializationFunction, isCritical = false) { + + // Check if critical + if(isCritical === true) { + + // Check if critical initialization functions don't exist + if(typeof Database.criticalInitializationFunctions === "undefined") + + // Create critical initialization functions + Database.criticalInitializationFunctions = []; + + // Append critical initialization function to list + Database.criticalInitializationFunctions.push(initializationFunction); + } + + // Otherwise + else { + + // Check if initialization functions don't exist + if(typeof Database.initializationFunctions === "undefined") + + // Create initialization functions + Database.initializationFunctions = []; + + // Append initialization function to list + Database.initializationFunctions.push(initializationFunction); + } + } + + // Create transaction + static createTransaction(objectStoreNames, type = Database.READ_MODE, durability = Database.RELAXED_DURABILITY, autocomplete = true) { + + // Check if object stores names isn't an array + if(Array.isArray(objectStoreNames) === false) { + + // Make object store names an array + objectStoreNames = [objectStoreNames]; + } + + // Initialize locked object stores + var lockedObjectStores = []; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return locking all specified object stores + return Promise.all(objectStoreNames.map(function(objectStoreName) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if object store is locked + if(Database.objectStoreLocks.has(objectStoreName) === true) { + + // Get current unlock event index + var index = Database.objectStoreUnlockEventIndex++; + + // Check if current unlock event index is at the max safe integer + if(index === Number.MAX_SAFE_INTEGER) + + // Reset unlock event index + Database.objectStoreUnlockEventIndex = 0; + + // Database object store unlock index event + $(Database.instance).on(Database.OBJECT_STORE_UNLOCK_EVENT + "." + index.toFixed(), function(event, unlockedObjectStoreName) { + + // Check if object store is unlocked + if(objectStoreName === unlockedObjectStoreName && Database.objectStoreLocks.has(objectStoreName) === false) { + + // Turn off database object store unlock index event + $(Database.instance).off(Database.OBJECT_STORE_UNLOCK_EVENT + "." + index.toFixed()); + + // Lock object store + Database.objectStoreLocks.add(objectStoreName); + + // Append locked object store to list + lockedObjectStores.push(objectStoreName); + + // Resolve + resolve(); + } + }); + } + + // Otherwise + else { + + // Lock object store + Database.objectStoreLocks.add(objectStoreName); + + // Append locked object store to list + lockedObjectStores.push(objectStoreName); + + // Resolve + resolve(); + } + }); + })).then(function() { + + // Try + try { + + // Create transaction + var transaction = Database.instance.transaction(objectStoreNames, type, { + + // Durability + "durability": durability + }); + } + + // Catch errors + catch(error) { + + // Go through all locked object stores + for(var i = 0; i < lockedObjectStores["length"]; ++i) { + + // Get object store + var objectStoreName = lockedObjectStores[i]; + + // Check if object store is locked + if(Database.objectStoreLocks.has(objectStoreName) === true) { + + // Unlock object store + Database.objectStoreLocks.delete(objectStoreName); + + // Trigger object store unlock event + $(Database.instance).trigger(Database.OBJECT_STORE_UNLOCK_EVENT, objectStoreName); + } + } + + // Reject + reject(); + + // Return + return; + } + + // Check if autocomplete + if(autocomplete === true) { + + // Transaction complete, abort, or error event + $(transaction).on("complete abort error", function() { + + // Unlock locked object stores + Database.unlockObjectStores(lockedObjectStores); + }); + } + + // Create database transaction + var databaseTransaction = new DatabaseTransaction(transaction, lockedObjectStores, autocomplete); + + // Resolve database transaction + resolve(databaseTransaction); + }); + }); + } + + // Commit transaction + static commitTransaction(transaction) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if transaction is completed + if(transaction.isCompleted() === true) { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Resolve + resolve(); + } + + // Otherwise check if transaction is aborted + else if(transaction.isAborted() === true) { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + } + + // Otherwise + else { + + // Transaction on complete + transaction.getTransaction()["oncomplete"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Resolve + resolve(); + }; + + // Transaction on error + transaction.getTransaction()["onerror"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + }; + + // Transaction on abort + transaction.getTransaction()["onabort"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + }; + + // Try + try { + + // Commit transaction + transaction.getTransaction().commit(); + } + + // Catch errors + catch(error) { + + } + } + }); + } + + // Cancel transaction + static abortTransaction(transaction) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if transaction is aborted + if(transaction.isAborted() === true) { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Resolve + resolve(); + } + + // Otherwise check if transaction is completed + else if(transaction.isCompleted() === true) { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + } + + // Otherwise + else { + + // Transaction on abort + transaction.getTransaction()["onabort"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Resolve + resolve(); + }; + + // Transaction on error + transaction.getTransaction()["onerror"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + }; + + // Transaction on complete + transaction.getTransaction()["oncomplete"] = function() { + + // Check if transaction isn't autocomplete + if(transaction.getAutocomplete() === false) { + + // Unlock transaction's object stores + Database.unlockObjectStores(transaction.getObjectStoreNames()); + } + + // Reject + reject(); + }; + + // Try + try { + + // Abort transaction + transaction.getTransaction().abort(); + } + + // Catch errors + catch(error) { + + } + } + }); + } + + // Get count + static getCount(objectStoreName, index = Database.NO_INDEX, range = Database.NO_RANGE, transaction = Database.CREATE_NEW_TRANSACTION) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Initialize count + var count; + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_MODE).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve count + resolve(count); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function(error) { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Get the object store + var objectStore = currentTransaction.getTransaction().objectStore(objectStoreName); + + // Create count request to get count from the object store + var countRequest = (index === Database.NO_INDEX) ? objectStore.count(range) : objectStore.index(index).count(range); + + // Count request on success + countRequest["onsuccess"] = function(event) { + + // Get result + var result = event["target"]["result"]; + + // Get count from result + count = result; + + // Check if not creating a new transaction + if(createNewTransaction === false) + + // Resolve count + resolve(count); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Get key paths + static getKeyPaths(objectStoreName, transaction = Database.CREATE_NEW_TRANSACTION) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Initialize key paths + var keyPaths = []; + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_MODE).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve key paths + resolve(keyPaths); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Create key cursor request to read from the object store + var keyCursorRequest = currentTransaction.getTransaction().objectStore(objectStoreName).openKeyCursor(); + + // Key cursor request on success + keyCursorRequest["onsuccess"] = function(event) { + + // Get result + var result = event["target"]["result"]; + + // Check if result exists + if(result !== Database.NO_RECORDS_MATCH_CURSOR_RESULT) { + + // Get key path from result + var keyPath = result["key"]; + + // Append key path to key paths + keyPaths.push(result["key"]); + + // Get next result + result.continue(); + } + + // Otherwise check if not creating a new transaction + else if(createNewTransaction === false) + + // Resolve key paths + resolve(keyPaths); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Get results + static getResults(objectStoreName, startPosition = Database.GET_ALL_RESULTS, count = Database.GET_ALL_RESULTS, index = Database.NO_INDEX, range = Database.NO_RANGE, direction = Database.FORWARD_DIRECTION, transaction = Database.CREATE_NEW_TRANSACTION) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Check if start position or count is invalid + if(startPosition < 0 || count < 0) { + + // Reject error + reject("Invalid parameters."); + + // Return + return; + } + + // Initialize results + var results = []; + + // Check if count is zero + if(count === 0) { + + // Resolve results + resolve(results); + + // Return + return; + } + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_MODE).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve results + resolve(results); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Get the object store + var objectStore = currentTransaction.getTransaction().objectStore(objectStoreName); + + // Create cursor request to read from the object store + var cursorRequest = (index === Database.NO_INDEX) ? objectStore.openKeyCursor(range, direction) : objectStore.index(index).openKeyCursor(range, direction); + + // Cursor request on success + var firstResult = true; + cursorRequest["onsuccess"] = function(event) { + + // Get result + var result = event["target"]["result"]; + + // Check if result exists + if(result !== Database.NO_RECORDS_MATCH_CURSOR_RESULT) { + + // Check if at the first result + if(firstResult === true) { + + // Clear first result + firstResult = false; + + // Check if a start position is provided and it's not zero + if(startPosition !== Database.GET_ALL_RESULTS && startPosition !== 0) { + + // Advance result to start position + result.advance(startPosition); + + // Return + return; + } + } + + // Get key path from result + var keyPath = result["primaryKey"]; + + // Create get request to read from the object store using the key path + var getRequest = objectStore.get(keyPath); + + // Get request on success + getRequest["onsuccess"] = function(event) { + + // Get request's result + var requestsResult = event["target"]["result"]; + + // Check if request's result exists + if(requestsResult !== Database.NO_RECORDS_MATCH_GET_RESULT) { + + // Set key path in result + requestsResult[Database.KEY_PATH_NAME] = keyPath; + + // Append result to results + results.push(requestsResult); + } + + // Check if not creating a new transaction, a count is provided, and it's currently at the count + if(createNewTransaction === false && count !== Database.GET_ALL_RESULTS && count === 0) + + // Resolve results + resolve(results); + }; + + // Check if a count isn't provided and currently not at the count + if(count === Database.GET_ALL_RESULTS || --count > 0) + + // Get next result + result.continue(); + } + + // Otherwise check if not creating a new transaction + else if(createNewTransaction === false) + + // Resolve results + resolve(results); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Get result + static getResult(objectStoreName, keyPath, transaction = Database.CREATE_NEW_TRANSACTION) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Initialize result + var result = Database.RESULT_NOT_FOUND; + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_MODE).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve result + resolve(result); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Create get request to read from the object store using the key path + var getRequest = currentTransaction.getTransaction().objectStore(objectStoreName).get(keyPath); + + // Get request on success + getRequest["onsuccess"] = function(event) { + + // Get request's result + var requestsResult = event["target"]["result"]; + + // Check if request's result exists + if(requestsResult !== Database.NO_RECORDS_MATCH_GET_RESULT) { + + // Set result to request's result + result = requestsResult; + + // Set key path in result + result[Database.KEY_PATH_NAME] = keyPath; + } + + // Check if not creating a new transaction + if(createNewTransaction === false) + + // Resolve result + resolve(result); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Save results + static saveResults(objectStoreName, results, resultsKeyPaths = [], transaction = Database.CREATE_NEW_TRANSACTION, durability = Database.RELAXED_DURABILITY) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Initialize new key paths + var newKeyPaths = []; + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_AND_WRITE_MODE, durability).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve new key paths + resolve(newKeyPaths); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Get the object store + var objectStore = currentTransaction.getTransaction().objectStore(objectStoreName); + + // Go through all result + for(var i = 0, j = 0; i < results["length"]; ++i) { + + // Check if result includes a key path + if(i < resultsKeyPaths["length"]) { + + // Create put request to write to the object store + var putRequest = objectStore.put(results[i], resultsKeyPaths[i]); + } + + // Otherwise + else { + + // Create put request to write to the object store + var putRequest = objectStore.put(results[i]); + } + + // Put request on success + putRequest["onsuccess"] = function(event) { + + // Get key path from result + var keyPath = event["target"]["result"]; + + // Appen key path to list + newKeyPaths.push(keyPath); + + // Check if not creating a new transaction + if(createNewTransaction === false) { + + // Check if all put requests were completed + if(++j === results["length"]) + + // Resolve new key paths + resolve(newKeyPaths); + } + }; + } + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Save result + static saveResult(objectStoreName, result, keyPath = Database.CREATE_NEW_KEY_PATH, transaction = Database.CREATE_NEW_TRANSACTION, durability = Database.RELAXED_DURABILITY) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Initialize new key path + var newKeyPath; + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_AND_WRITE_MODE, durability).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve new key path + resolve(newKeyPath); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Create put request to write to the object store + var putRequest = currentTransaction.getTransaction().objectStore(objectStoreName).put(result, keyPath); + + // Put request on success + putRequest["onsuccess"] = function(event) { + + // Get key path from result + var keyPath = event["target"]["result"]; + + // Set new key path to key path + newKeyPath = keyPath; + + // Check if not creating a new transaction + if(createNewTransaction === false) + + // Resolve new key path + resolve(newKeyPath); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Delete results + static deleteResults(objectStoreName, transaction = Database.CREATE_NEW_TRANSACTION, durability = Database.RELAXED_DURABILITY) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_AND_WRITE_MODE, durability).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve + resolve(); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Create clear request to delete everything from the object store + var clearRequest = currentTransaction.getTransaction().objectStore(objectStoreName).clear(); + + // Check if not creating a new transaction + if(createNewTransaction === false) { + + // Clear request on success + clearRequest["onsuccess"] = function(event) { + + // Resolve + resolve(); + }; + } + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Delete results with value + static deleteResultsWithValue(objectStoreName, index, range, transaction = Database.CREATE_NEW_TRANSACTION, durability = Database.RELAXED_DURABILITY) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_AND_WRITE_MODE, durability).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve + resolve(); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Get the object store + var objectStore = currentTransaction.getTransaction().objectStore(objectStoreName); + + // Create cursor request to write to the object store + var cursorRequest = objectStore.index(index).openCursor(range); + + // Cursor request on success + cursorRequest["onsuccess"] = function(event) { + + // Get result + var result = event["target"]["result"]; + + // Check if result exists + if(result !== Database.NO_RECORDS_MATCH_CURSOR_RESULT) { + + // Delete result + result.delete(); + + // Get next result + result.continue(); + } + + // Otherwise check if not creating a new transaction + else if(createNewTransaction === false) + + // Resolve + resolve(); + }; + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // Delete result + static deleteResult(objectStoreName, keyPath, transaction = Database.CREATE_NEW_TRANSACTION, durability = Database.RELAXED_DURABILITY) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if instance is invalid + if(Database.instance === Database.INVALID) { + + // Reject error + reject("Invalid database."); + + // Return + return; + } + + // Get if creating a new transaction + var createNewTransaction = transaction === Database.CREATE_NEW_TRANSACTION; + + // Get current transaction + var getCurrentTransaction = new Promise(function(resolve, reject) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Return creating a transaction + return Database.createTransaction(objectStoreName, Database.READ_AND_WRITE_MODE, durability).then(function(transaction) { + + // Resolve transaction + resolve(transaction); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve transaction + resolve(transaction); + } + }); + + // Return getting current transaction + return getCurrentTransaction.then(function(currentTransaction) { + + // Check if creating a new transaction + if(createNewTransaction === true) { + + // Current transaction on complete + currentTransaction.getTransaction()["oncomplete"] = function() { + + // Resolve + resolve(); + }; + } + + // Current transaction on error + currentTransaction.getTransaction()["onerror"] = function() { + + // Reject + reject(); + }; + + // Current transaction on abort + currentTransaction.getTransaction()["onabort"] = function() { + + // Reject + reject(); + }; + + // Create delete request to delete from the object store using the key path + var deleteRequest = currentTransaction.getTransaction().objectStore(objectStoreName).delete(keyPath); + + // Check if not creating a new transaction + if(createNewTransaction === false) { + + // Delete request on success + deleteRequest["onsuccess"] = function(event) { + + // Resolve + resolve(); + }; + } + + // Catch errors + }).catch(function(error) { + + // Reject + reject(); + }); + }); + } + + // To key path + static toKeyPath(string) { + + // Return string as a key path + return string.replace(Database.INVALID_KEY_PATH_PATTERN, Database.INVALID_KEY_PATH_REPLACEMENT); + } + + // Result not found + static get RESULT_NOT_FOUND() { + + // Return result not found + return null; + } + + // Create new key path + static get CREATE_NEW_KEY_PATH() { + + // Return create new key path + return undefined; + } + + // Read mode + static get READ_MODE() { + + // Return read mode + return "readonly"; + } + + // Read and write mode + static get READ_AND_WRITE_MODE() { + + // Return read and write mode + return "readwrite"; + } + + // Key path name + static get KEY_PATH_NAME() { + + // Return key path name + return "Key Path"; + } + + // Get all results + static get GET_ALL_RESULTS() { + + // Return get all results + return null; + } + + // No index + static get NO_INDEX() { + + // Return no index + return null; + } + + // Create new transaction + static get CREATE_NEW_TRANSACTION() { + + // Return create new transaction + return null; + } + + // No range + static get NO_RANGE() { + + // Return no range + return null; + } + + // Forward direction + static get FORWARD_DIRECTION() { + + // Return forward direction + return "next"; + } + + // Backward direction + static get BACKWARD_DIRECTION() { + + // Return backward direction + return "prev"; + } + + // No current version + static get NO_CURRENT_VERSION() { + + // Return no current version + return 0; + } + + // Relaxed durability + static get RELAXED_DURABILITY() { + + // Return relaxed durability + return "relaxed"; + } + + // Strict durability + static get STRICT_DURABILITY() { + + // Return strict durability + return "strict"; + } + + // Private + + // Unlock object stores + static unlockObjectStores(objectStoreNames) { + + // Go through all of the object stores + for(var i = 0; i < objectStoreNames["length"]; ++i) { + + // Get object store + let objectStoreName = objectStoreNames[i]; + + // Check if object store is locked + if(Database.objectStoreLocks.has(objectStoreName) === true) { + + // Set timeout + setTimeout(function() { + + // Unlock object store + Database.objectStoreLocks.delete(objectStoreName); + + // Trigger object store unlock event + $(Database.instance).trigger(Database.OBJECT_STORE_UNLOCK_EVENT, objectStoreName); + }, 0); + } + } + } + + // Invaid key path pattern + static get INVALID_KEY_PATH_PATTERN() { + + // Return invalid key path pattern + return /[ ']/gu; + } + + // Invalid key path replacement + static get INVALID_KEY_PATH_REPLACEMENT() { + + // Return invalid key path replacement + return ""; + } + + // Invalid + static get INVALID() { + + // Return invalid + return null; + } + + // Name + static get NAME() { + + // Return name + return "Database"; + } + + // Version one + static get VERSION_ONE() { + + // Return version one + return 1; + } + + // Version + static get VERSION() { + + // Return version + return 2; + } + + // No records match get result + static get NO_RECORDS_MATCH_GET_RESULT() { + + // Return no records match get result + return undefined; + } + + // No records match cursor result + static get NO_RECORDS_MATCH_CURSOR_RESULT() { + + // Return no records match cursor result + return null; + } + + // Object store unlock event + static get OBJECT_STORE_UNLOCK_EVENT() { + + // Return object store unlock event + return "DatabaseObjectStoreUnlockEvent"; + } +} + + +// Main function + +// Set global object's database +globalThis["Database"] = Database; diff --git a/scripts/database_transaction.js b/scripts/database_transaction.js new file mode 100755 index 0000000..d0d83a5 --- /dev/null +++ b/scripts/database_transaction.js @@ -0,0 +1,87 @@ +// Use strict +"use strict"; + + +// Classes + +// Database transaction class +class DatabaseTransaction { + + // Public + + // Constructor + constructor(transaction, objectStoreNames, autocomplete = true) { + + // Set transaction + this.transaction = transaction; + + // Set object store names + this.objectStoreNames = objectStoreNames; + + // Set autocomplete + this.autocomplete = autocomplete; + + // Set completed + this.completed = false; + + // Set aborted + this.aborted = false; + + // Set self + var self = this; + + // Transaction complete event + $(this.transaction).on("complete", function() { + + // Set completed + self.completed = true; + + // Transaction abort event + }).on("abort", function() { + + // Set aborted + self.aborted = true; + }); + } + + // Get transaction + getTransaction() { + + // Return transaction + return this.transaction; + } + + // Get object store names + getObjectStoreNames() { + + // Return object store names + return this.objectStoreNames; + } + + // Get autocomplete + getAutocomplete() { + + // Return autocomplete + return this.autocomplete; + } + + // Is completed + isCompleted() { + + // Return if completed + return this.completed === true; + } + + // Is aborted + isAborted() { + + // Return if aborted + return this.aborted === true; + } +} + + +// Main function + +// Set global object's database transaction +globalThis["DatabaseTransaction"] = DatabaseTransaction; diff --git a/scripts/emoji.js b/scripts/emoji.js new file mode 100755 index 0000000..880f47e --- /dev/null +++ b/scripts/emoji.js @@ -0,0 +1,1207 @@ +// Use strict +"use strict"; + + +// Classes + +// Emoji class +class Emoji { + + // Public + + // Encode + static encode(text) { + + // Get text as bytes + var bytes = (new TextEncoder()).encode(text); + + // Get number of extra bits + var numberOfExtraBits = Emoji.NUMBER_BASE - bytes["length"] * Common.BITS_IN_A_BYTE % Emoji.NUMBER_BASE; + + // Create bit writer + var bitWriter = new BitWriter(); + + // Append number of extra bits to bit writer + bitWriter.setBits(numberOfExtraBits, Emoji.NUMBER_BASE); + + // Append extra bits to bit writer + bitWriter.setBits(Emoji.EXTRA_BITS_VALUE, numberOfExtraBits); + + // Append bytes to bit writer + bitWriter.setBytes(bytes); + + // Create bit reader from bit writer + var bitReader = new BitReader(bitWriter.getBytes()); + + // Initialize emoji text + var emojiText = ""; + + // Loop through all bit groups in bit reader + while(true) { + + // Try + try { + + // Get index from bit group + var index = bitReader.getBits(Emoji.NUMBER_BASE); + + // Append glyph at index to emoji text + emojiText += Emoji.GLYPHS[index]; + } + + // Catch errors + catch(error) { + + // Break + break; + } + } + + // Return emoji text + return emojiText; + } + + // Decode + static decode(emojiText) { + + // Get emoji text as glyphs + var glyphs = Array.from(emojiText); + + // Initialize bit writer + var bitWriter = new BitWriter(); + + // Initialize number of bits + var numberOfBits = 0; + + // Go through all glyphs + for(var i = 0; i < glyphs["length"]; ++i) { + + // Check if glyph isn't valid + var index = Emoji.GLYPHS.indexOf(glyphs[i]); + + if(index === Common.INDEX_NOT_FOUND) { + + // Throw error + throw "Invalid emoji text."; + } + + // Append glyphs's index to bit writer + bitWriter.setBits(index, Emoji.NUMBER_BASE); + + // Update number of bits + numberOfBits += Emoji.NUMBER_BASE; + } + + // Create bit reader from bit writer + var bitReader = new BitReader(bitWriter.getBytes()); + + // Try + try { + + // Get number of extra bits from bit reader + var numberOfExtraBits = bitReader.getBits(Emoji.NUMBER_BASE); + } + + // Catch errors + catch(error) { + + // Throw error + throw "Invalid emoji text."; + } + + // Check if number of extra bits is invalid + if(numberOfExtraBits <= 0 || numberOfExtraBits > Emoji.NUMBER_BASE) { + + // Throw error + throw "Invalid emoji text."; + } + + // Try + try { + + // Get extra bits from bit reader + var extraBits = bitReader.getBits(numberOfExtraBits); + } + + // Catch errors + catch(error) { + + // Throw error + throw "Invalid emoji text."; + } + + // Check if extra bits is invalid + if(extraBits !== Emoji.EXTRA_BITS_VALUE) { + + // Throw error + throw "Invalid emoji text."; + } + + // Check if number of bits is invalid + if((numberOfBits - Emoji.NUMBER_BASE - numberOfExtraBits) % Common.BITS_IN_A_BYTE !== 0) { + + // Throw error + throw "Invalid emoji text."; + } + + // Get remaining bytes from bit reader + var bytes = bitReader.getBytes((numberOfBits - Emoji.NUMBER_BASE - numberOfExtraBits) / Common.BITS_IN_A_BYTE); + + // Return bytes as text + return (new TextDecoder("utf-8", {"fatal": true})).decode(bytes); + } + + // Private + + // Glyphs + static get GLYPHS() { + + // Return glyphs + return [ + "🎨", + "🎭", + "🧵", + "🧶", + "🪢", + "🪡", + "🏆", + "🥇", + "🏅", + "🥈", + "🥉", + "🎈", + "🎏", + "🎄", + "🎊", + "🧨", + "🎆", + "🎃", + "🎎", + "🎑", + "🎉", + "🎍", + "🧧", + "🎀", + "🎇", + "✨", + "🎋", + "🎫", + "🎐", + "🎁", + "🎯", + "🔮", + "🎴", + "🎲", + "🃏", + "🪁", + "🪄", + "🀄", + "🧿", + "🪆", + "🪅", + "🎱", + "🧩", + "🎰", + "🧸", + "🎮", + "🪀", + "🏈", + "🏸", + "⚾", + "🏀", + "🎳", + "🥊", + "🏏", + "🥌", + "🤿", + "🏑", + "🎣", + "⛳", + "🥏", + "🥅", + "🏒", + "🥍", + "🥋", + "🏓", + "🏉", + "🎽", + "🎿", + "🛷", + "⚽", + "🥎", + "🎾", + "🏐", + "🐸", + "🐤", + "🐦", + "🐔", + "🦤", + "🦆", + "🦅", + "🪶", + "🦩", + "🐥", + "🐣", + "🦉", + "🦜", + "🦚", + "🐧", + "🐓", + "🦢", + "🦃", + "🐜", + "🪲", + "🐛", + "🦋", + "🪳", + "🦗", + "🪰", + "🐝", + "🐞", + "🦠", + "🦟", + "🦂", + "🐌", + "🪱", + "🦡", + "🦇", + "🐻", + "🦫", + "🦬", + "🐗", + "🐪", + "🐈", + "🐱", + "🐄", + "🐮", + "🦌", + "🐕", + "🐶", + "🐘", + "🐑", + "🦊", + "🦒", + "🐐", + "🦍", + "🦮", + "🐹", + "🦔", + "🦛", + "🐎", + "🐴", + "🦘", + "🐨", + "🐆", + "🦁", + "🦙", + "🦣", + "🐒", + "🐵", + "🐁", + "🐭", + "🦧", + "🦦", + "🐂", + "🐼", + "🐾", + "🐖", + "🐷", + "🐽", + "🐩", + "🐇", + "🐰", + "🦝", + "🐏", + "🐀", + "🦏", + "🦨", + "🦥", + "🐅", + "🐯", + "🐫", + "🦄", + "🐃", + "🐺", + "🦓", + "🐡", + "🐬", + "🐟", + "🐙", + "🦭", + "🦈", + "🐚", + "🐳", + "🐠", + "🐋", + "🐊", + "🐉", + "🐲", + "🦎", + "🦕", + "🐍", + "🐢", + "🦖", + "🌼", + "💐", + "🌸", + "🌺", + "🌹", + "🌻", + "🌷", + "💮", + "🥀", + "🌵", + "🌳", + "🌲", + "🍂", + "🍀", + "🌿", + "🍃", + "🍁", + "🌴", + "🪴", + "🌱", + "🌾", + "🏴", + "🏁", + "🎌", + "🚩", + "🏺", + "🥢", + "🍴", + "🔪", + "🥄", + "🍼", + "🍺", + "🧃", + "🍾", + "🧋", + "🍻", + "🥂", + "🍸", + "🥤", + "🥛", + "☕", + "🧊", + "🧉", + "🍶", + "🍵", + "🫖", + "🍹", + "🥃", + "🍷", + "🍱", + "🍚", + "🍛", + "🍡", + "🥟", + "🍥", + "🥠", + "🍤", + "🥮", + "🍢", + "🍙", + "🍘", + "🍠", + "🍝", + "🍜", + "🍣", + "🥡", + "🍌", + "🫐", + "🍒", + "🥥", + "🍇", + "🍏", + "🥝", + "🍋", + "🥭", + "🍈", + "🫒", + "🍑", + "🍐", + "🍍", + "🍎", + "🍓", + "🍊", + "🍅", + "🍉", + "🦀", + "🦞", + "🦪", + "🦐", + "🦑", + "🥓", + "🥯", + "🥖", + "🥣", + "🍞", + "🌯", + "🧈", + "🥫", + "🧀", + "🍳", + "🥐", + "🥩", + "🥚", + "🧆", + "🫓", + "🫕", + "🍟", + "🥗", + "🍔", + "🌭", + "🍖", + "🥞", + "🍕", + "🍿", + "🍲", + "🍗", + "🥨", + "🧂", + "🥪", + "🥘", + "🥙", + "🌮", + "🫔", + "🧇", + "🎂", + "🍬", + "🍫", + "🍪", + "🧁", + "🍮", + "🍩", + "🍯", + "🍨", + "🍭", + "🥧", + "🍧", + "🍰", + "🍦", + "🥑", + "🫑", + "🥦", + "🥕", + "🌰", + "🥒", + "🌽", + "🍆", + "🧄", + "🥬", + "🍄", + "🧅", + "🥜", + "🥔", + "📘", + "🔖", + "📑", + "📚", + "📕", + "📗", + "📒", + "📰", + "📓", + "📔", + "📖", + "📙", + "📄", + "📃", + "📜", + "🎒", + "🩰", + "👙", + "🧢", + "🩲", + "👝", + "🧥", + "👑", + "👗", + "🥿", + "💎", + "👓", + "🧤", + "🥽", + "🎓", + "👜", + "👠", + "🥾", + "👖", + "👘", + "🥼", + "💄", + "👞", + "🪖", + "👔", + "🩱", + "📿", + "👛", + "💍", + "👟", + "🦺", + "🥻", + "🧣", + "🩳", + "🧦", + "🩴", + "🎩", + "👕", + "👢", + "👚", + "👒", + "👡", + "🧮", + "🔋", + "💽", + "📀", + "🔌", + "💾", + "💻", + "💿", + "🧺", + "🛁", + "🧹", + "🪣", + "🪑", + "🚪", + "🛗", + "🧯", + "🧴", + "🪞", + "🪤", + "🪒", + "🧻", + "🧷", + "🛒", + "🚿", + "🧼", + "🧽", + "🚽", + "🪥", + "🪟", + "📷", + "📸", + "🎬", + "🪔", + "🔦", + "💡", + "🔍", + "🔎", + "🎥", + "🏮", + "📺", + "📼", + "📹", + "🔑", + "🔒", + "🔐", + "🔏", + "🔓", + "📪", + "📫", + "📩", + "📧", + "📥", + "📨", + "📭", + "📬", + "📤", + "📦", + "📮", + "🩹", + "🩸", + "💊", + "🩺", + "💉", + "💹", + "🪙", + "💳", + "💵", + "💶", + "💰", + "💸", + "💷", + "🧾", + "💴", + "🎧", + "🎤", + "🎵", + "🎶", + "🎼", + "📻", + "🪗", + "🪕", + "🥁", + "🎸", + "🪘", + "🎹", + "🎷", + "🎺", + "🎻", + "📊", + "💼", + "📅", + "📇", + "📉", + "📈", + "📋", + "📁", + "📂", + "📎", + "📌", + "📍", + "📏", + "📆", + "📐", + "🚬", + "🪦", + "🗿", + "🪧", + "📠", + "📱", + "📲", + "📟", + "📞", + "🧬", + "🔬", + "🧫", + "📡", + "🔭", + "🧪", + "🔔", + "🔕", + "📢", + "📣", + "🔇", + "📯", + "🔊", + "🔈", + "🔉", + "🪓", + "🪃", + "🏹", + "🪚", + "🔨", + "🪝", + "🪜", + "🔗", + "🧲", + "🔩", + "🪛", + "🧰", + "🔫", + "🦯", + "🔧", + "📝", + "🫀", + "🦴", + "🧠", + "👂", + "🦻", + "👀", + "💪", + "🦶", + "🦵", + "🫁", + "🦾", + "🦿", + "👄", + "👃", + "👅", + "🦷", + "🤛", + "👊", + "✊", + "🤜", + "👎", + "👍", + "🤚", + "✋", + "🖖", + "👋", + "🤙", + "🤞", + "🤟", + "👌", + "🤌", + "🤏", + "🤘", + "💅", + "🤳", + "👇", + "👈", + "👉", + "👆", + "🖕", + "👏", + "🙏", + "🤝", + "👐", + "🤲", + "🙌", + "👶", + "👦", + "🧒", + "👧", + "👨", + "🧓", + "👴", + "👵", + "🧑", + "🧔", + "👱", + "👩", + "🕺", + "👯", + "🧗", + "💇", + "💆", + "🧖", + "🧎", + "🏃", + "🧍", + "🚶", + "💃", + "👼", + "🧝", + "🧚", + "🧞", + "🧙", + "🧜", + "🤶", + "🎅", + "🦸", + "🦹", + "🧛", + "🧟", + "🧏", + "🙇", + "🤦", + "🙍", + "🙅", + "🙆", + "🙎", + "🙋", + "🤷", + "💁", + "🤱", + "👷", + "💂", + "🥷", + "🤵", + "👳", + "👲", + "👰", + "👮", + "🤰", + "🤴", + "👸", + "🧕", + "🏇", + "🤼", + "🚴", + "🤸", + "🤺", + "🤹", + "🚵", + "🤾", + "🤽", + "🚣", + "🏄", + "🏊", + "🏂", + "👥", + "👤", + "👣", + "🫂", + "💢", + "💓", + "🖤", + "💙", + "💣", + "💔", + "🤎", + "💥", + "💨", + "💫", + "💚", + "💗", + "💟", + "💘", + "💝", + "💯", + "💋", + "💌", + "🧡", + "💜", + "💞", + "💖", + "💬", + "💦", + "💭", + "💕", + "🤍", + "💛", + "💤", + "😘", + "😗", + "😚", + "😙", + "🥰", + "😍", + "🥲", + "🤩", + "😧", + "😰", + "😲", + "😖", + "😕", + "😢", + "😞", + "😓", + "😱", + "😮", + "😨", + "😳", + "😦", + "😯", + "😭", + "😣", + "🥺", + "😥", + "🙁", + "😫", + "😩", + "😟", + "🥱", + "👽", + "👾", + "🤡", + "👻", + "👺", + "👹", + "💩", + "🤖", + "🧐", + "🤓", + "😎", + "🤭", + "🤗", + "🤫", + "🤔", + "🤠", + "🥸", + "🥳", + "😠", + "👿", + "😤", + "🤬", + "😡", + "💀", + "😈", + "😑", + "😶", + "🤨", + "🙄", + "😬", + "🤥", + "😐", + "😏", + "😒", + "🤐", + "🤤", + "😔", + "😌", + "😴", + "😪", + "😁", + "😂", + "😀", + "😃", + "😄", + "😅", + "😆", + "🤣", + "🙂", + "😇", + "😊", + "🙃", + "😉", + "😋", + "😛", + "🤑", + "😝", + "😜", + "🤪", + "🥶", + "🤯", + "🤮", + "🤕", + "😷", + "🤒", + "🥵", + "😵", + "🤢", + "🤧", + "🥴", + "🆎", + "🆑", + "🆒", + "🆓", + "🆔", + "🔤", + "🔡", + "🔠", + "🔢", + "🔣", + "🉑", + "🈸", + "🉐", + "🈹", + "🈚", + "🈁", + "🈶", + "🈵", + "🈺", + "🈴", + "🈲", + "🈯", + "🈳", + "🆕", + "🆖", + "🆗", + "🆘", + "🆙", + "🆚", + "🔙", + "🔃", + "🔄", + "🔚", + "🔛", + "🔜", + "🔝", + "📶", + "🔆", + "🎦", + "🔅", + "🔽", + "⏬", + "⏩", + "⏪", + "⏫", + "📴", + "🔁", + "🔂", + "🔀", + "🔼", + "📳", + "💱", + "💲", + "➗", + "➖", + "➕", + "✅", + "❌", + "❎", + "➰", + "➿", + "⭕", + "🔰", + "📛", + "🔱", + "❗", + "❓", + "❕", + "❔", + "🏧", + "🚼", + "🛄", + "🛃", + "🛅", + "🚮", + "🚹", + "🛂", + "🚰", + "🚻", + "🚾", + "♿", + "🚺", + "🚸", + "🚱", + "🚳", + "⛔", + "🚯", + "📵", + "🔞", + "🚷", + "🚭", + "🚫", + "🏦", + "🧱", + "🏰", + "🏪", + "🏬", + "🏭", + "🏥", + "🏨", + "🏠", + "🏡", + "🛖", + "🏯", + "🏣", + "🏩", + "🏢", + "🏤", + "🪨", + "🏫", + "🗽", + "🗼", + "💒", + "🪵", + "🗻", + "🌋", + "💈", + "🌉", + "🎠", + "🎪", + "🌆", + "🎡", + "🌁", + "⛲", + "🌃", + "🎢", + "🌅", + "🌄", + "🌇", + "⛺", + "⛪", + "🛕", + "🕋", + "🕌", + "🕍", + "🌂", + "🌙", + "🌀", + "💧", + "🔥", + "🌓", + "🌛", + "🌕", + "🌝", + "🌟", + "⚡", + "🌗", + "🌜", + "🌌", + "🌑", + "🌚", + "🌈", + "🪐", + "🌠", + "⛄", + "⭐", + "⛅", + "🌞", + "☔", + "🌘", + "🌖", + "🌊", + "🌒", + "🌔", + "⏰", + "🕗", + "🕣", + "🕚", + "🕦", + "🕔", + "🕠", + "🕓", + "🕟", + "⌛", + "⏳", + "🕘", + "🕤", + "🕐", + "🕜", + "🕖", + "🕢", + "🕕", + "🕡", + "🕙", + "🕥", + "🕒", + "🕞", + "🕛", + "🕧", + "🕑", + "🕝", + "⌚", + "🚡", + "🛬", + "🛫", + "🛸", + "🚁", + "🚠", + "🪂", + "🚀", + "💺", + "🚟", + "🚑", + "🚛", + "🚗", + "🛺", + "🚲", + "🚅", + "🚌", + "🚏", + "🚧", + "🚚", + "🚒", + "⛽", + "🚄", + "🚥", + "🛴", + "🚈", + "🚂", + "🦽", + "🚇", + "🚐", + "🚝", + "🦼", + "🛵", + "🚞", + "🚘", + "🚍", + "🚔", + "🚖", + "🛻", + "🚓", + "🚨", + "🚃", + "🛼", + "🛹", + "🚙", + "🚉" + ]; + } + + // Number base + static get NUMBER_BASE() { + + // Return number base + return Math.ceil(Math.log2(Emoji.GLYPHS["length"])); + } + + // Extra bits value + static get EXTRA_BITS_VALUE() { + + // Return extra bits value + return 0; + } +} + + +// Main function + +// Set global object's emoji +globalThis["Emoji"] = Emoji; diff --git a/scripts/extension.js b/scripts/extension.js new file mode 100755 index 0000000..949f88b --- /dev/null +++ b/scripts/extension.js @@ -0,0 +1,410 @@ +// Use strict +"use strict"; + + +// Classes + +// Extension class +class Extension { + + // Public + + // Initialize + static initialize() { + + // Get URL parameters + var urlParameters = Common.getUrlParameters(); + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if URL parameters contains a request + if("Request" in urlParameters === true) { + + // Set close when done + Extension.closeWhenDone = true; + + // Try + try { + + // Process URL parameters as a request + Extension.processRequest(urlParameters, true); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + + // Return + return; + } + } + + // Otherwise + else { + + // Clear close when done + Extension.closeWhenDone = false; + } + + // Resolve + resolve(); + }); + } + + // Get requests + static getRequests() { + + // Return requests + return Extension.requests; + } + + // Allow interrupt on close + static allowInterruptOnClose() { + + // Set can interrupt close + Extension.canInterruptClose = true; + } + + // Prevent interrupt on close + static preventInterruptOnClose() { + + // Clear can interrupt close + Extension.canInterruptClose = false; + } + + // Get close when done + static getCloseWhenDone() { + + // Return close when done + return Extension.closeWhenDone; + } + + // No transaction amount + static get NO_TRANSACTION_AMOUNT() { + + // Return no transaction amount + return null; + } + + // No transaction message + static get NO_TRANSACTION_MESSAGE() { + + // Return no transation message + return null; + } + + // Private + + // Process request + static processRequest(request, makeFirstRequest = false) { + + // Check request + switch(request["Request"]) { + + // Start transaction + case "Start Transaction": + + // Check if recipient address isn't valid + if("Recipient Address" in request === false || typeof request["Recipient Address"] !== "string" || request["Recipient Address"]["length"] === 0) { + + // Throw error + throw Language.getDefaultTranslation('Invalid recipient address.'); + } + + // Otherwise check if amount isn't valid + else if("Amount" in request === true && request["Amount"] !== Extension.NO_TRANSACTION_AMOUNT && (Common.isNumberString(request["Amount"]) === false || Common.getNumberStringPrecision(request["Amount"]) > Extension.MAXIMUM_AMOUNT_PRECISION || parseFloat(Common.removeTrailingZeros(request["Amount"])) < Extension.MINIMUM_AMOUNT)) { + + // Throw error + throw Language.getDefaultTranslation('Invalid amount.'); + } + + // Otherwise check if message isn't valid + else if("Message" in request === true && request["Message"] !== Extension.NO_TRANSACTION_MESSAGE && typeof request["Message"] !== "string") { + + // Throw error + throw Language.getDefaultTranslation('Invalid message.'); + } + + // Otherwise + else { + + // Get request information + var requestInformation = { + + // Name + "Name": SendPaymentSection.NAME, + + // State + "State": { + + // Elements states + [Section.STATE_ELEMENTS_STATES_NAME]: [ + + // Back + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Forward + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Recipient address + { + "Tag": "INPUT", + "Focused": false, + "Value": ProtocolHandler.standardizeUrlProtocol(request["Recipient Address"]), + "Selection Start": Section.NO_VALUE, + "Selection End": Section.NO_VALUE, + "Selection Direction": Section.NO_VALUE + }, + + // Scan + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Wallet + { + "Tag": "SELECT", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Amount + { + "Tag": "INPUT", + "Focused": false, + "Value": ("Amount" in request === true && request["Amount"] !== Extension.NO_TRANSACTION_AMOUNT) ? (new BigNumber(request["Amount"])).toFixed() : Section.NO_VALUE, + "Selection Start": Section.NO_VALUE, + "Selection End": Section.NO_VALUE, + "Selection Direction": Section.NO_VALUE + }, + + // All + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Value + { + "Tag": "INPUT", + "Focused": false, + "Value": Section.NO_VALUE, + "Selection Start": Section.NO_VALUE, + "Selection End": Section.NO_VALUE, + "Selection Direction": Section.NO_VALUE + }, + + // Base fee + { + "Tag": "INPUT", + "Focused": false, + "Value": Section.NO_VALUE, + "Selection Start": Section.NO_VALUE, + "Selection End": Section.NO_VALUE, + "Selection Direction": Section.NO_VALUE + }, + + // Default base fee + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Message + { + "Tag": "INPUT", + "Focused": false, + "Value": ("Message" in request === true && request["Message"] !== Extension.NO_TRANSACTION_MESSAGE) ? request["Message"] : Section.NO_VALUE, + "Selection Start": Section.NO_VALUE, + "Selection End": Section.NO_VALUE, + "Selection Direction": Section.NO_VALUE + }, + + // Send + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + }, + + // Cancel + { + "Tag": "BUTTON", + "Focused": false, + "Value": Section.NO_VALUE + } + ] + } + }; + + // Check if making first request + if(makeFirstRequest === true) { + + // Add request information to beginning of list + Extension.requests.unshift(requestInformation); + } + + // Otherwise + else { + + // Append request information to list + Extension.requests.push(requestInformation); + } + + // Trigger extension request receive event + $(document).trigger(Extension.REQUEST_RECEIVE_EVENT); + } + + // Break + break; + + // Default + default: + + // Throw error + throw Language.getDefaultTranslation('Invalid request.'); + } + } + + // Maximum amount precision + static get MAXIMUM_AMOUNT_PRECISION() { + + // Return maximum amount precision + return Math.log10(Consensus.VALUE_NUMBER_BASE); + } + + // Minimum amount + static get MINIMUM_AMOUNT() { + + // Return minimum amount + return 1 / Consensus.VALUE_NUMBER_BASE; + } + + // Request receive event + static get REQUEST_RECEIVE_EVENT() { + + // Return request receive event + return "ExtensionRequestReceiveEvent"; + } +} + + +// Main function + +// Set global object's extension +globalThis["Extension"] = Extension; + +// Initialize extension requests +Extension.requests = []; + +// Initialize extension can interrupt close +Extension.canInterruptClose = false; + +// Window before unload event +$(window).on("beforeunload", function(event) { + + // Check if extension can interrupt close and extension requests exist + if(Extension.canInterruptClose === true && Extension.getRequests()["length"] !== 0) { + + // Try + try { + + // Prevent default + event.preventDefault(); + + // Stop propagation + event.stopPropagation(); + event.stopImmediatePropagation(); + + // Check if one extension request exists + if(Extension.getRequests()["length"] === 1) { + + // Return message + return event["originalEvent"]["returnValue"] = Language.getTranslation('Are you sure you want to exit? There\'s a remaining transaction.'); + } + + // Otherwise + else { + + // Return message + return event["originalEvent"]["returnValue"] = Language.getTranslation('Are you sure you want to exit? There\'s remaining transactions.'); + } + } + + // Catch errors + catch(error) { + + } + } +}); + +// Check if is a Firefox or Safari extension +if(Common.isExtension() === true && typeof browser !== "undefined") { + + // Message event + browser["runtime"]["onMessage"].addListener(function(request, sender, sendResponse) { + + // Check if request is from the content script + if(sender["id"] === browser["runtime"]["id"] && "frameId" in sender === true && typeof request === "object" && request !== null && "Wallet Type" in request === true && "Network Type" in request === true && "Request" in request === true) { + + // Check if request's wallet type is the current wallet type and network type is the current network type + if(request["Wallet Type"] === Consensus.walletTypeToText(Consensus.getWalletType()) && request["Network Type"] === Consensus.networkTypeToText(Consensus.getNetworkType())) { + + // Try + try { + + // Process extension request + Extension.processRequest(request); + } + + // Catch errors + catch(error) { + + } + } + } + }); +} + +// Otherwise check if is a Chrome extension +else if(Common.isExtension() === true && typeof chrome !== "undefined") { + + // Message event + chrome["runtime"]["onMessage"].addListener(function(request, sender, sendResponse) { + + // Check if request is from the content script + if(sender["id"] === chrome["runtime"]["id"] && "frameId" in sender === true && typeof request === "object" && request !== null && "Wallet Type" in request === true && "Network Type" in request === true && "Request" in request === true) { + + // Check if request's wallet type is the current wallet type and network type is the current network type + if(request["Wallet Type"] === Consensus.walletTypeToText(Consensus.getWalletType()) && request["Network Type"] === Consensus.networkTypeToText(Consensus.getNetworkType())) { + + // Try + try { + + // Process extension request + Extension.processRequest(request); + } + + // Catch errors + catch(error) { + + } + } + } + }); +} diff --git a/scripts/fatal_error.js b/scripts/fatal_error.js new file mode 100755 index 0000000..b922e89 --- /dev/null +++ b/scripts/fatal_error.js @@ -0,0 +1,123 @@ +// Use strict +"use strict"; + + +// Classes + +// Fatal error class +class FatalError { + + // Public + + // Constructor + constructor(errorType = FatalError.UNKNOWN_ERROR) { + + // Check if a fatal error hasn't occurred + if(FatalError.errorOccurred === false) { + + // Set that a fatal error occurred + FatalError.errorOccurred = true; + + // Check error type + switch(errorType) { + + // Local storage error + case FatalError.LOCAL_STORAGE_ERROR: + + // Log error + console.log("A local storage error occurred."); + + // Break + break; + + // Database error + case FatalError.DATABASE_ERROR: + + // Log error + console.log("A database error occurred."); + + // Break + break; + + // Unknown error and default + case FatalError.UNKNOWN_ERROR: + default: + + // Log error + console.log("An unknown error occurred."); + + // Break + break; + } + + // Log stack trace + console.trace(); + + // Prevent extension from interrupting on close + Extension.preventInterruptOnClose(); + + // Check if not an extension and not loading from file + if(Common.isExtension() === false && location["protocol"] !== Common.FILE_PROTOCOL) { + + // Go to error page + location.replace(((location["protocol"] === Common.HTTPS_PROTOCOL) ? Common.HTTPS_PROTOCOL : Common.HTTP_PROTOCOL) + "//" + location["hostname"] + FatalError.ERROR_PAGE_URL); + } + + // Otherwise + else { + + // Close + window.close(); + } + } + } + + // Local storage error + static get LOCAL_STORAGE_ERROR() { + + // Return local storage error + return 0; + } + + // Database error + static get DATABASE_ERROR() { + + // Return database error + return FatalError.LOCAL_STORAGE_ERROR + 1; + } + + // Unknown error + static get UNKNOWN_ERROR() { + + // Return unknown error + return FatalError.DATABASE_ERROR + 1; + } + + // Private + + // Error page URL + static get ERROR_PAGE_URL() { + + // Return error page URL + return "/errors/error.html"; + } +} + + +// Main function + +// Set global object's fatal error +globalThis["FatalError"] = FatalError; + +// Set fatal error error occurred +FatalError.errorOccurred = false; + +// Window error event +$(window).on("error", function() { + + // Check if using application error handler + if(usingApplicationErrorHandler() === true) + + // Trigger a fatal error + new FatalError(FatalError.UNKNOWN_ERROR); +}); diff --git a/scripts/focus.js b/scripts/focus.js new file mode 100755 index 0000000..b565f46 --- /dev/null +++ b/scripts/focus.js @@ -0,0 +1,240 @@ +// Use strict +"use strict"; + + +// Classes + +// Focus class +class Focus { + + // Public + + // Constructor + constructor() { + + // Set focus stack to empty array + this.focusStack = []; + + // Set self + var self = this; + + // Document input focus event + $(document).on("focus", "input", function() { + + // Get input + var input = $(this); + + // Check if input isn't having its focus restored + if(input.hasClass("focused") === false) { + + // Select input + input.select(); + } + }); + + // Document key down event + $(document).on("keydown", function(event) { + + // Check if is an extension and a popup or is an app + if((Common.isExtension() === true && Common.isPopup() === true) || Common.isApp() === true) { + + // Check if key tab is pressed + if(event["which"] === "\t".charCodeAt(0)) { + + // Get all focusable elements + var focusableElements = $("button:not([tabindex=\"-1\"]):not(:disabled):visible, input:not([tabindex=\"-1\"]):not(:disabled):visible, select:not([tabindex=\"-1\"]):not(:disabled):visible, a:not([tabindex=\"-1\"]):not(:disabled):visible").filter(function() { + + // Return if not hidden + return $(this).closest(".hide")["length"] === 0; + }); + + // Check if elements are focusable + if(focusableElements["length"] !== 0) { + + // Check if no element is focused + if($(":focus")["length"] === 0) { + + // Check if tabbing forward + if(event["shiftKey"] === false) { + + // Focus on first focusable element + focusableElements.first().focus(); + } + + // Otherwise + else { + + // Focus on last first focusable element + focusableElements.last().focus(); + } + + // Prevent default; + event.preventDefault(); + } + + // Otherwise check if tabbing forward and focused on the last focusable element + else if(event["shiftKey"] === false && focusableElements.last().is(":focus") === true) { + + // Check if other elements are focusable + if(focusableElements["length"] > 1) { + + // Focus on first focusable element + focusableElements.first().focus(); + } + + // Prevent default; + event.preventDefault(); + } + + // Otherwise check if tabbing backward and focused on the first focusable element + else if(event["shiftKey"] === true && focusableElements.first().is(":focus") === true) { + + // Check if other elements are focusable + if(focusableElements["length"] > 1) { + + // Focus on last first focusable element + focusableElements.last().focus(); + } + + // Prevent default; + event.preventDefault(); + } + } + + // Otherwise + else { + + // Prevent default; + event.preventDefault(); + } + } + } + }); + } + + // Save + save(blur) { + + // Get focused element + var focusedElement = $(":focus"); + + // Check if no elements are focused + if(focusedElement["length"] === 0) { + + // Add no focus element to focus stack + this.focusStack.push(Focus.NO_FOCUS); + + // Return no focus + return Focus.NO_FOCUS; + } + + // Otherwise + else { + + // Add focused element to the focus stack + this.focusStack.push(focusedElement.first()); + + // Check if blurring + if(blur === true) { + + // Remove focus from element + focusedElement.blur(); + + // Remove selection + Focus.removeSelection(); + } + + // Return focused element + return focusedElement; + } + } + + // Restore + restore(blurIfNoState) { + + // Check if focus stack isn't empty + if(this.focusStack["length"] > 0) { + + // Get focused element from the focus stack and remove it from the focus stack + var focusedElement = this.focusStack.pop(); + + // Check if focused element isn't no focus + if(focusedElement !== Focus.NO_FOCUS) { + + // Set that focused element is having its focus restored + focusedElement.addClass("focused"); + + // Focus on focused element + focusedElement.focus(); + + // Set that focused element isn't having its focus restored + focusedElement.removeClass("focused"); + } + + // Otherwise check if blurring if no state + else if(blurIfNoState === true) + + // Blur focused element + $(":focus").blur(); + } + } + + // Delete + delete() { + + // Check if focus stack isn't empty + if(this.focusStack["length"] > 0) + + // Remove next focused element from the focus stack + this.focusStack.pop(); + } + + // Delete all + deleteAll() { + + // Set focus stack to empty array + this.focusStack = []; + } + + // Remove selection + static removeSelection() { + + // Check if get selection is supported + if(typeof getSelection !== "undefined") { + + // Get current selection + var currentSelection = getSelection(); + + // Check if current selection empty is supported + if(typeof currentSelection.empty !== "undefined") + + // Empty current selection + currentSelection.empty(); + + // Otherwise check if current selection remove all ranges is supported + else if(typeof currentSelection.removeAllRanges !== "undefined") + + // Remove all ranges from the current selection + currentSelection.removeAllRanges(); + } + + // Otherwise check if selection is supported and selection empty is supported + else if(typeof document["selection"] !== "undefined" && typeof document["selection"].empty !== "undefined") + + // Empty selection + document["selection"].empty(); + } + + // No focus + static get NO_FOCUS() { + + // Return no focus + return null; + } +} + + +// Main function + +// Set global object's focus +globalThis["Focus"] = Focus; diff --git a/scripts/glMatrix license.txt b/scripts/glMatrix license.txt new file mode 100755 index 0000000..3a96fca --- /dev/null +++ b/scripts/glMatrix license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2015-2021, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/scripts/glMatrix-3.4.1.js b/scripts/glMatrix-3.4.1.js new file mode 100755 index 0000000..0ff67be --- /dev/null +++ b/scripts/glMatrix-3.4.1.js @@ -0,0 +1,7860 @@ + +/*! +@fileoverview gl-matrix - High performance matrix and vector operations +@author Brandon Jones +@author Colin MacKenzie IV +@version 3.4.1 + +Copyright (c) 2015-2021, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +(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.glMatrix = {})); +})(this, (function (exports) { 'use strict'; + + /** + * Common utilities + * @module glMatrix + */ + // Configuration Constants + var EPSILON = 0.000001; + var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; + var RANDOM = Math.random; + var ANGLE_ORDER = "zyx"; + /** + * Sets the type of array used when creating new vectors and matrices + * + * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array + */ + + function setMatrixArrayType(type) { + ARRAY_TYPE = type; + } + var degree = Math.PI / 180; + /** + * Convert Degree To Radian + * + * @param {Number} a Angle in Degrees + */ + + function toRadian(a) { + return a * degree; + } + /** + * Tests whether or not the arguments have approximately the same value, within an absolute + * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less + * than or equal to 1.0, and a relative tolerance is used for larger values) + * + * @param {Number} a The first number to test. + * @param {Number} b The second number to test. + * @returns {Boolean} True if the numbers are approximately equal, false otherwise. + */ + + function equals$9(a, b) { + return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); + } + if (!Math.hypot) Math.hypot = function () { + var y = 0, + i = arguments.length; + + while (i--) { + y += arguments[i] * arguments[i]; + } + + return Math.sqrt(y); + }; + + var common = /*#__PURE__*/Object.freeze({ + __proto__: null, + EPSILON: EPSILON, + get ARRAY_TYPE () { return ARRAY_TYPE; }, + RANDOM: RANDOM, + ANGLE_ORDER: ANGLE_ORDER, + setMatrixArrayType: setMatrixArrayType, + toRadian: toRadian, + equals: equals$9 + }); + + /** + * 2x2 Matrix + * @module mat2 + */ + + /** + * Creates a new identity mat2 + * + * @returns {mat2} a new 2x2 matrix + */ + + function create$8() { + var out = new ARRAY_TYPE(4); + + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + } + + out[0] = 1; + out[3] = 1; + return out; + } + /** + * Creates a new mat2 initialized with values from an existing matrix + * + * @param {ReadonlyMat2} a matrix to clone + * @returns {mat2} a new 2x2 matrix + */ + + function clone$8(a) { + var out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + /** + * Copy the values from one mat2 to another + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the source matrix + * @returns {mat2} out + */ + + function copy$8(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + /** + * Set a mat2 to the identity matrix + * + * @param {mat2} out the receiving matrix + * @returns {mat2} out + */ + + function identity$5(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } + /** + * Create a new mat2 with the given values + * + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m10 Component in column 1, row 0 position (index 2) + * @param {Number} m11 Component in column 1, row 1 position (index 3) + * @returns {mat2} out A new 2x2 matrix + */ + + function fromValues$8(m00, m01, m10, m11) { + var out = new ARRAY_TYPE(4); + out[0] = m00; + out[1] = m01; + out[2] = m10; + out[3] = m11; + return out; + } + /** + * Set the components of a mat2 to the given values + * + * @param {mat2} out the receiving matrix + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m10 Component in column 1, row 0 position (index 2) + * @param {Number} m11 Component in column 1, row 1 position (index 3) + * @returns {mat2} out + */ + + function set$8(out, m00, m01, m10, m11) { + out[0] = m00; + out[1] = m01; + out[2] = m10; + out[3] = m11; + return out; + } + /** + * Transpose the values of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the source matrix + * @returns {mat2} out + */ + + function transpose$2(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache + // some values + if (out === a) { + var a1 = a[1]; + out[1] = a[2]; + out[2] = a1; + } else { + out[0] = a[0]; + out[1] = a[2]; + out[2] = a[1]; + out[3] = a[3]; + } + + return out; + } + /** + * Inverts a mat2 + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the source matrix + * @returns {mat2} out + */ + + function invert$5(out, a) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; // Calculate the determinant + + var det = a0 * a3 - a2 * a1; + + if (!det) { + return null; + } + + det = 1.0 / det; + out[0] = a3 * det; + out[1] = -a1 * det; + out[2] = -a2 * det; + out[3] = a0 * det; + return out; + } + /** + * Calculates the adjugate of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the source matrix + * @returns {mat2} out + */ + + function adjoint$2(out, a) { + // Caching this value is necessary if out == a + var a0 = a[0]; + out[0] = a[3]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a0; + return out; + } + /** + * Calculates the determinant of a mat2 + * + * @param {ReadonlyMat2} a the source matrix + * @returns {Number} determinant of a + */ + + function determinant$3(a) { + return a[0] * a[3] - a[2] * a[1]; + } + /** + * Multiplies two mat2's + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the first operand + * @param {ReadonlyMat2} b the second operand + * @returns {mat2} out + */ + + function multiply$8(out, a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + return out; + } + /** + * Rotates a mat2 by the given angle + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2} out + */ + + function rotate$4(out, a, rad) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + return out; + } + /** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the matrix to rotate + * @param {ReadonlyVec2} v the vec2 to scale the matrix by + * @returns {mat2} out + **/ + + function scale$8(out, a, v) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var v0 = v[0], + v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + return out; + } + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat2.identity(dest); + * mat2.rotate(dest, dest, rad); + * + * @param {mat2} out mat2 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2} out + */ + + function fromRotation$4(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = -s; + out[3] = c; + return out; + } + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat2.identity(dest); + * mat2.scale(dest, dest, vec); + * + * @param {mat2} out mat2 receiving operation result + * @param {ReadonlyVec2} v Scaling vector + * @returns {mat2} out + */ + + function fromScaling$3(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = v[1]; + return out; + } + /** + * Returns a string representation of a mat2 + * + * @param {ReadonlyMat2} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + + function str$8(a) { + return "mat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")"; + } + /** + * Returns Frobenius norm of a mat2 + * + * @param {ReadonlyMat2} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + + function frob$3(a) { + return Math.hypot(a[0], a[1], a[2], a[3]); + } + /** + * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix + * @param {ReadonlyMat2} L the lower triangular matrix + * @param {ReadonlyMat2} D the diagonal matrix + * @param {ReadonlyMat2} U the upper triangular matrix + * @param {ReadonlyMat2} a the input matrix to factorize + */ + + function LDU(L, D, U, a) { + L[2] = a[2] / a[0]; + U[0] = a[0]; + U[1] = a[1]; + U[3] = a[3] - L[2] * U[1]; + return [L, D, U]; + } + /** + * Adds two mat2's + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the first operand + * @param {ReadonlyMat2} b the second operand + * @returns {mat2} out + */ + + function add$8(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; + } + /** + * Subtracts matrix b from matrix a + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the first operand + * @param {ReadonlyMat2} b the second operand + * @returns {mat2} out + */ + + function subtract$6(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; + } + /** + * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyMat2} a The first matrix. + * @param {ReadonlyMat2} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function exactEquals$8(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; + } + /** + * Returns whether or not the matrices have approximately the same elements in the same position. + * + * @param {ReadonlyMat2} a The first matrix. + * @param {ReadonlyMat2} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function equals$8(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)); + } + /** + * Multiply each element of the matrix by a scalar. + * + * @param {mat2} out the receiving matrix + * @param {ReadonlyMat2} a the matrix to scale + * @param {Number} b amount to scale the matrix's elements by + * @returns {mat2} out + */ + + function multiplyScalar$3(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; + } + /** + * Adds two mat2's after multiplying each element of the second operand by a scalar value. + * + * @param {mat2} out the receiving vector + * @param {ReadonlyMat2} a the first operand + * @param {ReadonlyMat2} b the second operand + * @param {Number} scale the amount to scale b's elements by before adding + * @returns {mat2} out + */ + + function multiplyScalarAndAdd$3(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + out[3] = a[3] + b[3] * scale; + return out; + } + /** + * Alias for {@link mat2.multiply} + * @function + */ + + var mul$8 = multiply$8; + /** + * Alias for {@link mat2.subtract} + * @function + */ + + var sub$6 = subtract$6; + + var mat2 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$8, + clone: clone$8, + copy: copy$8, + identity: identity$5, + fromValues: fromValues$8, + set: set$8, + transpose: transpose$2, + invert: invert$5, + adjoint: adjoint$2, + determinant: determinant$3, + multiply: multiply$8, + rotate: rotate$4, + scale: scale$8, + fromRotation: fromRotation$4, + fromScaling: fromScaling$3, + str: str$8, + frob: frob$3, + LDU: LDU, + add: add$8, + subtract: subtract$6, + exactEquals: exactEquals$8, + equals: equals$8, + multiplyScalar: multiplyScalar$3, + multiplyScalarAndAdd: multiplyScalarAndAdd$3, + mul: mul$8, + sub: sub$6 + }); + + /** + * 2x3 Matrix + * @module mat2d + * @description + * A mat2d contains six elements defined as: + *
    +   * [a, b,
    +   *  c, d,
    +   *  tx, ty]
    +   * 
    + * This is a short form for the 3x3 matrix: + *
    +   * [a, b, 0,
    +   *  c, d, 0,
    +   *  tx, ty, 1]
    +   * 
    + * The last column is ignored so the array is shorter and operations are faster. + */ + + /** + * Creates a new identity mat2d + * + * @returns {mat2d} a new 2x3 matrix + */ + + function create$7() { + var out = new ARRAY_TYPE(6); + + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[4] = 0; + out[5] = 0; + } + + out[0] = 1; + out[3] = 1; + return out; + } + /** + * Creates a new mat2d initialized with values from an existing matrix + * + * @param {ReadonlyMat2d} a matrix to clone + * @returns {mat2d} a new 2x3 matrix + */ + + function clone$7(a) { + var out = new ARRAY_TYPE(6); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + } + /** + * Copy the values from one mat2d to another + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the source matrix + * @returns {mat2d} out + */ + + function copy$7(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + } + /** + * Set a mat2d to the identity matrix + * + * @param {mat2d} out the receiving matrix + * @returns {mat2d} out + */ + + function identity$4(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + } + /** + * Create a new mat2d with the given values + * + * @param {Number} a Component A (index 0) + * @param {Number} b Component B (index 1) + * @param {Number} c Component C (index 2) + * @param {Number} d Component D (index 3) + * @param {Number} tx Component TX (index 4) + * @param {Number} ty Component TY (index 5) + * @returns {mat2d} A new mat2d + */ + + function fromValues$7(a, b, c, d, tx, ty) { + var out = new ARRAY_TYPE(6); + out[0] = a; + out[1] = b; + out[2] = c; + out[3] = d; + out[4] = tx; + out[5] = ty; + return out; + } + /** + * Set the components of a mat2d to the given values + * + * @param {mat2d} out the receiving matrix + * @param {Number} a Component A (index 0) + * @param {Number} b Component B (index 1) + * @param {Number} c Component C (index 2) + * @param {Number} d Component D (index 3) + * @param {Number} tx Component TX (index 4) + * @param {Number} ty Component TY (index 5) + * @returns {mat2d} out + */ + + function set$7(out, a, b, c, d, tx, ty) { + out[0] = a; + out[1] = b; + out[2] = c; + out[3] = d; + out[4] = tx; + out[5] = ty; + return out; + } + /** + * Inverts a mat2d + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the source matrix + * @returns {mat2d} out + */ + + function invert$4(out, a) { + var aa = a[0], + ab = a[1], + ac = a[2], + ad = a[3]; + var atx = a[4], + aty = a[5]; + var det = aa * ad - ab * ac; + + if (!det) { + return null; + } + + det = 1.0 / det; + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + } + /** + * Calculates the determinant of a mat2d + * + * @param {ReadonlyMat2d} a the source matrix + * @returns {Number} determinant of a + */ + + function determinant$2(a) { + return a[0] * a[3] - a[1] * a[2]; + } + /** + * Multiplies two mat2d's + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the first operand + * @param {ReadonlyMat2d} b the second operand + * @returns {mat2d} out + */ + + function multiply$7(out, a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + out[4] = a0 * b4 + a2 * b5 + a4; + out[5] = a1 * b4 + a3 * b5 + a5; + return out; + } + /** + * Rotates a mat2d by the given angle + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2d} out + */ + + function rotate$3(out, a, rad) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5]; + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + out[4] = a4; + out[5] = a5; + return out; + } + /** + * Scales the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the matrix to translate + * @param {ReadonlyVec2} v the vec2 to scale the matrix by + * @returns {mat2d} out + **/ + + function scale$7(out, a, v) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5]; + var v0 = v[0], + v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + out[4] = a4; + out[5] = a5; + return out; + } + /** + * Translates the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the matrix to translate + * @param {ReadonlyVec2} v the vec2 to translate the matrix by + * @returns {mat2d} out + **/ + + function translate$3(out, a, v) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5]; + var v0 = v[0], + v1 = v[1]; + out[0] = a0; + out[1] = a1; + out[2] = a2; + out[3] = a3; + out[4] = a0 * v0 + a2 * v1 + a4; + out[5] = a1 * v0 + a3 * v1 + a5; + return out; + } + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.rotate(dest, dest, rad); + * + * @param {mat2d} out mat2d receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2d} out + */ + + function fromRotation$3(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = -s; + out[3] = c; + out[4] = 0; + out[5] = 0; + return out; + } + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.scale(dest, dest, vec); + * + * @param {mat2d} out mat2d receiving operation result + * @param {ReadonlyVec2} v Scaling vector + * @returns {mat2d} out + */ + + function fromScaling$2(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = v[1]; + out[4] = 0; + out[5] = 0; + return out; + } + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat2d.identity(dest); + * mat2d.translate(dest, dest, vec); + * + * @param {mat2d} out mat2d receiving operation result + * @param {ReadonlyVec2} v Translation vector + * @returns {mat2d} out + */ + + function fromTranslation$3(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = v[0]; + out[5] = v[1]; + return out; + } + /** + * Returns a string representation of a mat2d + * + * @param {ReadonlyMat2d} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + + function str$7(a) { + return "mat2d(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ")"; + } + /** + * Returns Frobenius norm of a mat2d + * + * @param {ReadonlyMat2d} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + + function frob$2(a) { + return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], 1); + } + /** + * Adds two mat2d's + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the first operand + * @param {ReadonlyMat2d} b the second operand + * @returns {mat2d} out + */ + + function add$7(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + return out; + } + /** + * Subtracts matrix b from matrix a + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the first operand + * @param {ReadonlyMat2d} b the second operand + * @returns {mat2d} out + */ + + function subtract$5(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + out[4] = a[4] - b[4]; + out[5] = a[5] - b[5]; + return out; + } + /** + * Multiply each element of the matrix by a scalar. + * + * @param {mat2d} out the receiving matrix + * @param {ReadonlyMat2d} a the matrix to scale + * @param {Number} b amount to scale the matrix's elements by + * @returns {mat2d} out + */ + + function multiplyScalar$2(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + return out; + } + /** + * Adds two mat2d's after multiplying each element of the second operand by a scalar value. + * + * @param {mat2d} out the receiving vector + * @param {ReadonlyMat2d} a the first operand + * @param {ReadonlyMat2d} b the second operand + * @param {Number} scale the amount to scale b's elements by before adding + * @returns {mat2d} out + */ + + function multiplyScalarAndAdd$2(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + out[3] = a[3] + b[3] * scale; + out[4] = a[4] + b[4] * scale; + out[5] = a[5] + b[5] * scale; + return out; + } + /** + * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyMat2d} a The first matrix. + * @param {ReadonlyMat2d} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function exactEquals$7(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5]; + } + /** + * Returns whether or not the matrices have approximately the same elements in the same position. + * + * @param {ReadonlyMat2d} a The first matrix. + * @param {ReadonlyMat2d} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function equals$7(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)); + } + /** + * Alias for {@link mat2d.multiply} + * @function + */ + + var mul$7 = multiply$7; + /** + * Alias for {@link mat2d.subtract} + * @function + */ + + var sub$5 = subtract$5; + + var mat2d = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$7, + clone: clone$7, + copy: copy$7, + identity: identity$4, + fromValues: fromValues$7, + set: set$7, + invert: invert$4, + determinant: determinant$2, + multiply: multiply$7, + rotate: rotate$3, + scale: scale$7, + translate: translate$3, + fromRotation: fromRotation$3, + fromScaling: fromScaling$2, + fromTranslation: fromTranslation$3, + str: str$7, + frob: frob$2, + add: add$7, + subtract: subtract$5, + multiplyScalar: multiplyScalar$2, + multiplyScalarAndAdd: multiplyScalarAndAdd$2, + exactEquals: exactEquals$7, + equals: equals$7, + mul: mul$7, + sub: sub$5 + }); + + /** + * 3x3 Matrix + * @module mat3 + */ + + /** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ + + function create$6() { + var out = new ARRAY_TYPE(9); + + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + + out[0] = 1; + out[4] = 1; + out[8] = 1; + return out; + } + /** + * Copies the upper-left 3x3 values into the given mat3. + * + * @param {mat3} out the receiving 3x3 matrix + * @param {ReadonlyMat4} a the source 4x4 matrix + * @returns {mat3} out + */ + + function fromMat4$1(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[4]; + out[4] = a[5]; + out[5] = a[6]; + out[6] = a[8]; + out[7] = a[9]; + out[8] = a[10]; + return out; + } + /** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {ReadonlyMat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ + + function clone$6(a) { + var out = new ARRAY_TYPE(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + } + /** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the source matrix + * @returns {mat3} out + */ + + function copy$6(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + } + /** + * Create a new mat3 with the given values + * + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m02 Component in column 0, row 2 position (index 2) + * @param {Number} m10 Component in column 1, row 0 position (index 3) + * @param {Number} m11 Component in column 1, row 1 position (index 4) + * @param {Number} m12 Component in column 1, row 2 position (index 5) + * @param {Number} m20 Component in column 2, row 0 position (index 6) + * @param {Number} m21 Component in column 2, row 1 position (index 7) + * @param {Number} m22 Component in column 2, row 2 position (index 8) + * @returns {mat3} A new mat3 + */ + + function fromValues$6(m00, m01, m02, m10, m11, m12, m20, m21, m22) { + var out = new ARRAY_TYPE(9); + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m10; + out[4] = m11; + out[5] = m12; + out[6] = m20; + out[7] = m21; + out[8] = m22; + return out; + } + /** + * Set the components of a mat3 to the given values + * + * @param {mat3} out the receiving matrix + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m02 Component in column 0, row 2 position (index 2) + * @param {Number} m10 Component in column 1, row 0 position (index 3) + * @param {Number} m11 Component in column 1, row 1 position (index 4) + * @param {Number} m12 Component in column 1, row 2 position (index 5) + * @param {Number} m20 Component in column 2, row 0 position (index 6) + * @param {Number} m21 Component in column 2, row 1 position (index 7) + * @param {Number} m22 Component in column 2, row 2 position (index 8) + * @returns {mat3} out + */ + + function set$6(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) { + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m10; + out[4] = m11; + out[5] = m12; + out[6] = m20; + out[7] = m21; + out[8] = m22; + return out; + } + /** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ + + function identity$3(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + } + /** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the source matrix + * @returns {mat3} out + */ + + function transpose$1(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], + a02 = a[2], + a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; + } + /** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the source matrix + * @returns {mat3} out + */ + + function invert$3(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2]; + var a10 = a[3], + a11 = a[4], + a12 = a[5]; + var a20 = a[6], + a21 = a[7], + a22 = a[8]; + var b01 = a22 * a11 - a12 * a21; + var b11 = -a22 * a10 + a12 * a20; + var b21 = a21 * a10 - a11 * a20; // Calculate the determinant + + var det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + + det = 1.0 / det; + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; + } + /** + * Calculates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the source matrix + * @returns {mat3} out + */ + + function adjoint$1(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2]; + var a10 = a[3], + a11 = a[4], + a12 = a[5]; + var a20 = a[6], + a21 = a[7], + a22 = a[8]; + out[0] = a11 * a22 - a12 * a21; + out[1] = a02 * a21 - a01 * a22; + out[2] = a01 * a12 - a02 * a11; + out[3] = a12 * a20 - a10 * a22; + out[4] = a00 * a22 - a02 * a20; + out[5] = a02 * a10 - a00 * a12; + out[6] = a10 * a21 - a11 * a20; + out[7] = a01 * a20 - a00 * a21; + out[8] = a00 * a11 - a01 * a10; + return out; + } + /** + * Calculates the determinant of a mat3 + * + * @param {ReadonlyMat3} a the source matrix + * @returns {Number} determinant of a + */ + + function determinant$1(a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2]; + var a10 = a[3], + a11 = a[4], + a12 = a[5]; + var a20 = a[6], + a21 = a[7], + a22 = a[8]; + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + } + /** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the first operand + * @param {ReadonlyMat3} b the second operand + * @returns {mat3} out + */ + + function multiply$6(out, a, b) { + var a00 = a[0], + a01 = a[1], + a02 = a[2]; + var a10 = a[3], + a11 = a[4], + a12 = a[5]; + var a20 = a[6], + a21 = a[7], + a22 = a[8]; + var b00 = b[0], + b01 = b[1], + b02 = b[2]; + var b10 = b[3], + b11 = b[4], + b12 = b[5]; + var b20 = b[6], + b21 = b[7], + b22 = b[8]; + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; + } + /** + * Translate a mat3 by the given vector + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the matrix to translate + * @param {ReadonlyVec2} v vector to translate by + * @returns {mat3} out + */ + + function translate$2(out, a, v) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a10 = a[3], + a11 = a[4], + a12 = a[5], + a20 = a[6], + a21 = a[7], + a22 = a[8], + x = v[0], + y = v[1]; + out[0] = a00; + out[1] = a01; + out[2] = a02; + out[3] = a10; + out[4] = a11; + out[5] = a12; + out[6] = x * a00 + y * a10 + a20; + out[7] = x * a01 + y * a11 + a21; + out[8] = x * a02 + y * a12 + a22; + return out; + } + /** + * Rotates a mat3 by the given angle + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ + + function rotate$2(out, a, rad) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a10 = a[3], + a11 = a[4], + a12 = a[5], + a20 = a[6], + a21 = a[7], + a22 = a[8], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = c * a00 + s * a10; + out[1] = c * a01 + s * a11; + out[2] = c * a02 + s * a12; + out[3] = c * a10 - s * a00; + out[4] = c * a11 - s * a01; + out[5] = c * a12 - s * a02; + out[6] = a20; + out[7] = a21; + out[8] = a22; + return out; + } + /** + * Scales the mat3 by the dimensions in the given vec2 + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the matrix to rotate + * @param {ReadonlyVec2} v the vec2 to scale the matrix by + * @returns {mat3} out + **/ + + function scale$6(out, a, v) { + var x = v[0], + y = v[1]; + out[0] = x * a[0]; + out[1] = x * a[1]; + out[2] = x * a[2]; + out[3] = y * a[3]; + out[4] = y * a[4]; + out[5] = y * a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + } + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.translate(dest, dest, vec); + * + * @param {mat3} out mat3 receiving operation result + * @param {ReadonlyVec2} v Translation vector + * @returns {mat3} out + */ + + function fromTranslation$2(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = v[0]; + out[7] = v[1]; + out[8] = 1; + return out; + } + /** + * Creates a matrix from a given angle + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.rotate(dest, dest, rad); + * + * @param {mat3} out mat3 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ + + function fromRotation$2(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = 0; + out[3] = -s; + out[4] = c; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + } + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat3.identity(dest); + * mat3.scale(dest, dest, vec); + * + * @param {mat3} out mat3 receiving operation result + * @param {ReadonlyVec2} v Scaling vector + * @returns {mat3} out + */ + + function fromScaling$1(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = v[1]; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + } + /** + * Copies the values from a mat2d into a mat3 + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat2d} a the matrix to copy + * @returns {mat3} out + **/ + + function fromMat2d(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = 0; + out[3] = a[2]; + out[4] = a[3]; + out[5] = 0; + out[6] = a[4]; + out[7] = a[5]; + out[8] = 1; + return out; + } + /** + * Calculates a 3x3 matrix from the given quaternion + * + * @param {mat3} out mat3 receiving operation result + * @param {ReadonlyQuat} q Quaternion to create matrix from + * + * @returns {mat3} out + */ + + function fromQuat$1(out, q) { + var x = q[0], + y = q[1], + z = q[2], + w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var yx = y * x2; + var yy = y * y2; + var zx = z * x2; + var zy = z * y2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + out[0] = 1 - yy - zz; + out[3] = yx - wz; + out[6] = zx + wy; + out[1] = yx + wz; + out[4] = 1 - xx - zz; + out[7] = zy - wx; + out[2] = zx - wy; + out[5] = zy + wx; + out[8] = 1 - xx - yy; + return out; + } + /** + * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix + * + * @param {mat3} out mat3 receiving operation result + * @param {ReadonlyMat4} a Mat4 to derive the normal matrix from + * + * @returns {mat3} out + */ + + function normalFromMat4(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + var a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + var a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15]; + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; // Calculate the determinant + + var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + + det = 1.0 / det; + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + return out; + } + /** + * Generates a 2D projection matrix with the given bounds + * + * @param {mat3} out mat3 frustum matrix will be written into + * @param {number} width Width of your gl context + * @param {number} height Height of gl context + * @returns {mat3} out + */ + + function projection(out, width, height) { + out[0] = 2 / width; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = -2 / height; + out[5] = 0; + out[6] = -1; + out[7] = 1; + out[8] = 1; + return out; + } + /** + * Returns a string representation of a mat3 + * + * @param {ReadonlyMat3} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + + function str$6(a) { + return "mat3(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ")"; + } + /** + * Returns Frobenius norm of a mat3 + * + * @param {ReadonlyMat3} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + + function frob$1(a) { + return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); + } + /** + * Adds two mat3's + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the first operand + * @param {ReadonlyMat3} b the second operand + * @returns {mat3} out + */ + + function add$6(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + out[8] = a[8] + b[8]; + return out; + } + /** + * Subtracts matrix b from matrix a + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the first operand + * @param {ReadonlyMat3} b the second operand + * @returns {mat3} out + */ + + function subtract$4(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + out[4] = a[4] - b[4]; + out[5] = a[5] - b[5]; + out[6] = a[6] - b[6]; + out[7] = a[7] - b[7]; + out[8] = a[8] - b[8]; + return out; + } + /** + * Multiply each element of the matrix by a scalar. + * + * @param {mat3} out the receiving matrix + * @param {ReadonlyMat3} a the matrix to scale + * @param {Number} b amount to scale the matrix's elements by + * @returns {mat3} out + */ + + function multiplyScalar$1(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + out[6] = a[6] * b; + out[7] = a[7] * b; + out[8] = a[8] * b; + return out; + } + /** + * Adds two mat3's after multiplying each element of the second operand by a scalar value. + * + * @param {mat3} out the receiving vector + * @param {ReadonlyMat3} a the first operand + * @param {ReadonlyMat3} b the second operand + * @param {Number} scale the amount to scale b's elements by before adding + * @returns {mat3} out + */ + + function multiplyScalarAndAdd$1(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + out[3] = a[3] + b[3] * scale; + out[4] = a[4] + b[4] * scale; + out[5] = a[5] + b[5] * scale; + out[6] = a[6] + b[6] * scale; + out[7] = a[7] + b[7] * scale; + out[8] = a[8] + b[8] * scale; + return out; + } + /** + * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyMat3} a The first matrix. + * @param {ReadonlyMat3} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function exactEquals$6(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8]; + } + /** + * Returns whether or not the matrices have approximately the same elements in the same position. + * + * @param {ReadonlyMat3} a The first matrix. + * @param {ReadonlyMat3} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function equals$6(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5], + a6 = a[6], + a7 = a[7], + a8 = a[8]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7], + b8 = b[8]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8)); + } + /** + * Alias for {@link mat3.multiply} + * @function + */ + + var mul$6 = multiply$6; + /** + * Alias for {@link mat3.subtract} + * @function + */ + + var sub$4 = subtract$4; + + var mat3 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$6, + fromMat4: fromMat4$1, + clone: clone$6, + copy: copy$6, + fromValues: fromValues$6, + set: set$6, + identity: identity$3, + transpose: transpose$1, + invert: invert$3, + adjoint: adjoint$1, + determinant: determinant$1, + multiply: multiply$6, + translate: translate$2, + rotate: rotate$2, + scale: scale$6, + fromTranslation: fromTranslation$2, + fromRotation: fromRotation$2, + fromScaling: fromScaling$1, + fromMat2d: fromMat2d, + fromQuat: fromQuat$1, + normalFromMat4: normalFromMat4, + projection: projection, + str: str$6, + frob: frob$1, + add: add$6, + subtract: subtract$4, + multiplyScalar: multiplyScalar$1, + multiplyScalarAndAdd: multiplyScalarAndAdd$1, + exactEquals: exactEquals$6, + equals: equals$6, + mul: mul$6, + sub: sub$4 + }); + + /** + * 4x4 Matrix
    Format: column-major, when typed out it looks like row-major
    The matrices are being post multiplied. + * @module mat4 + */ + + /** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ + + function create$5() { + var out = new ARRAY_TYPE(16); + + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + } + + out[0] = 1; + out[5] = 1; + out[10] = 1; + out[15] = 1; + return out; + } + /** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {ReadonlyMat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ + + function clone$5(a) { + var out = new ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + /** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the source matrix + * @returns {mat4} out + */ + + function copy$5(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + /** + * Create a new mat4 with the given values + * + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m02 Component in column 0, row 2 position (index 2) + * @param {Number} m03 Component in column 0, row 3 position (index 3) + * @param {Number} m10 Component in column 1, row 0 position (index 4) + * @param {Number} m11 Component in column 1, row 1 position (index 5) + * @param {Number} m12 Component in column 1, row 2 position (index 6) + * @param {Number} m13 Component in column 1, row 3 position (index 7) + * @param {Number} m20 Component in column 2, row 0 position (index 8) + * @param {Number} m21 Component in column 2, row 1 position (index 9) + * @param {Number} m22 Component in column 2, row 2 position (index 10) + * @param {Number} m23 Component in column 2, row 3 position (index 11) + * @param {Number} m30 Component in column 3, row 0 position (index 12) + * @param {Number} m31 Component in column 3, row 1 position (index 13) + * @param {Number} m32 Component in column 3, row 2 position (index 14) + * @param {Number} m33 Component in column 3, row 3 position (index 15) + * @returns {mat4} A new mat4 + */ + + function fromValues$5(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { + var out = new ARRAY_TYPE(16); + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m03; + out[4] = m10; + out[5] = m11; + out[6] = m12; + out[7] = m13; + out[8] = m20; + out[9] = m21; + out[10] = m22; + out[11] = m23; + out[12] = m30; + out[13] = m31; + out[14] = m32; + out[15] = m33; + return out; + } + /** + * Set the components of a mat4 to the given values + * + * @param {mat4} out the receiving matrix + * @param {Number} m00 Component in column 0, row 0 position (index 0) + * @param {Number} m01 Component in column 0, row 1 position (index 1) + * @param {Number} m02 Component in column 0, row 2 position (index 2) + * @param {Number} m03 Component in column 0, row 3 position (index 3) + * @param {Number} m10 Component in column 1, row 0 position (index 4) + * @param {Number} m11 Component in column 1, row 1 position (index 5) + * @param {Number} m12 Component in column 1, row 2 position (index 6) + * @param {Number} m13 Component in column 1, row 3 position (index 7) + * @param {Number} m20 Component in column 2, row 0 position (index 8) + * @param {Number} m21 Component in column 2, row 1 position (index 9) + * @param {Number} m22 Component in column 2, row 2 position (index 10) + * @param {Number} m23 Component in column 2, row 3 position (index 11) + * @param {Number} m30 Component in column 3, row 0 position (index 12) + * @param {Number} m31 Component in column 3, row 1 position (index 13) + * @param {Number} m32 Component in column 3, row 2 position (index 14) + * @param {Number} m33 Component in column 3, row 3 position (index 15) + * @returns {mat4} out + */ + + function set$5(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m03; + out[4] = m10; + out[5] = m11; + out[6] = m12; + out[7] = m13; + out[8] = m20; + out[9] = m21; + out[10] = m22; + out[11] = m23; + out[12] = m30; + out[13] = m31; + out[14] = m32; + out[15] = m33; + return out; + } + /** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ + + function identity$2(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the source matrix + * @returns {mat4} out + */ + + function transpose(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a12 = a[6], + a13 = a[7]; + var a23 = a[11]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; + } + /** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the source matrix + * @returns {mat4} out + */ + + function invert$2(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + var a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + var a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15]; + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; // Calculate the determinant + + var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + + det = 1.0 / det; + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + return out; + } + /** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the source matrix + * @returns {mat4} out + */ + + function adjoint(out, a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + var a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + var a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15]; + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; + out[0] = a11 * b11 - a12 * b10 + a13 * b09; + out[1] = a02 * b10 - a01 * b11 - a03 * b09; + out[2] = a31 * b05 - a32 * b04 + a33 * b03; + out[3] = a22 * b04 - a21 * b05 - a23 * b03; + out[4] = a12 * b08 - a10 * b11 - a13 * b07; + out[5] = a00 * b11 - a02 * b08 + a03 * b07; + out[6] = a32 * b02 - a30 * b05 - a33 * b01; + out[7] = a20 * b05 - a22 * b02 + a23 * b01; + out[8] = a10 * b10 - a11 * b08 + a13 * b06; + out[9] = a01 * b08 - a00 * b10 - a03 * b06; + out[10] = a30 * b04 - a31 * b02 + a33 * b00; + out[11] = a21 * b02 - a20 * b04 - a23 * b00; + out[12] = a11 * b07 - a10 * b09 - a12 * b06; + out[13] = a00 * b09 - a01 * b07 + a02 * b06; + out[14] = a31 * b01 - a30 * b03 - a32 * b00; + out[15] = a20 * b03 - a21 * b01 + a22 * b00; + return out; + } + /** + * Calculates the determinant of a mat4 + * + * @param {ReadonlyMat4} a the source matrix + * @returns {Number} determinant of a + */ + + function determinant(a) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + var a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + var a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15]; + var b0 = a00 * a11 - a01 * a10; + var b1 = a00 * a12 - a02 * a10; + var b2 = a01 * a12 - a02 * a11; + var b3 = a20 * a31 - a21 * a30; + var b4 = a20 * a32 - a22 * a30; + var b5 = a21 * a32 - a22 * a31; + var b6 = a00 * b5 - a01 * b4 + a02 * b3; + var b7 = a10 * b5 - a11 * b4 + a12 * b3; + var b8 = a20 * b2 - a21 * b1 + a22 * b0; + var b9 = a30 * b2 - a31 * b1 + a32 * b0; // Calculate the determinant + + return a13 * b6 - a03 * b7 + a33 * b8 - a23 * b9; + } + /** + * Multiplies two mat4s + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the first operand + * @param {ReadonlyMat4} b the second operand + * @returns {mat4} out + */ + + function multiply$5(out, a, b) { + var a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3]; + var a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + var a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + var a30 = a[12], + a31 = a[13], + a32 = a[14], + a33 = a[15]; // Cache only the current line of the second matrix + + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3]; + out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return out; + } + /** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to translate + * @param {ReadonlyVec3} v vector to translate by + * @returns {mat4} out + */ + + function translate$1(out, a, v) { + var x = v[0], + y = v[1], + z = v[2]; + var a00, a01, a02, a03; + var a10, a11, a12, a13; + var a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a10 = a[4]; + a11 = a[5]; + a12 = a[6]; + a13 = a[7]; + a20 = a[8]; + a21 = a[9]; + a22 = a[10]; + a23 = a[11]; + out[0] = a00; + out[1] = a01; + out[2] = a02; + out[3] = a03; + out[4] = a10; + out[5] = a11; + out[6] = a12; + out[7] = a13; + out[8] = a20; + out[9] = a21; + out[10] = a22; + out[11] = a23; + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; + } + /** + * Scales the mat4 by the dimensions in the given vec3 not using vectorization + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to scale + * @param {ReadonlyVec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ + + function scale$5(out, a, v) { + var x = v[0], + y = v[1], + z = v[2]; + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + } + /** + * Rotates a mat4 by the given angle around the given axis + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {ReadonlyVec3} axis the axis to rotate around + * @returns {mat4} out + */ + + function rotate$1(out, a, rad, axis) { + var x = axis[0], + y = axis[1], + z = axis[2]; + var len = Math.hypot(x, y, z); + var s, c, t; + var a00, a01, a02, a03; + var a10, a11, a12, a13; + var a20, a21, a22, a23; + var b00, b01, b02; + var b10, b11, b12; + var b20, b21, b22; + + if (len < EPSILON) { + return null; + } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + a00 = a[0]; + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a10 = a[4]; + a11 = a[5]; + a12 = a[6]; + a13 = a[7]; + a20 = a[8]; + a21 = a[9]; + a22 = a[10]; + a23 = a[11]; // Construct the elements of the rotation matrix + + b00 = x * x * t + c; + b01 = y * x * t + z * s; + b02 = z * x * t - y * s; + b10 = x * y * t - z * s; + b11 = y * y * t + c; + b12 = z * y * t + x * s; + b20 = x * z * t + y * s; + b21 = y * z * t - x * s; + b22 = z * z * t + c; // Perform rotation-specific matrix multiplication + + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { + // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + return out; + } + /** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function rotateX$3(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a10 = a[4]; + var a11 = a[5]; + var a12 = a[6]; + var a13 = a[7]; + var a20 = a[8]; + var a21 = a[9]; + var a22 = a[10]; + var a23 = a[11]; + + if (a !== out) { + // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } // Perform axis-specific matrix multiplication + + + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; + } + /** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function rotateY$3(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a00 = a[0]; + var a01 = a[1]; + var a02 = a[2]; + var a03 = a[3]; + var a20 = a[8]; + var a21 = a[9]; + var a22 = a[10]; + var a23 = a[11]; + + if (a !== out) { + // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } // Perform axis-specific matrix multiplication + + + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; + } + /** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function rotateZ$3(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a00 = a[0]; + var a01 = a[1]; + var a02 = a[2]; + var a03 = a[3]; + var a10 = a[4]; + var a11 = a[5]; + var a12 = a[6]; + var a13 = a[7]; + + if (a !== out) { + // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } // Perform axis-specific matrix multiplication + + + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; + } + /** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, dest, vec); + * + * @param {mat4} out mat4 receiving operation result + * @param {ReadonlyVec3} v Translation vector + * @returns {mat4} out + */ + + function fromTranslation$1(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + /** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.scale(dest, dest, vec); + * + * @param {mat4} out mat4 receiving operation result + * @param {ReadonlyVec3} v Scaling vector + * @returns {mat4} out + */ + + function fromScaling(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = v[1]; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = v[2]; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Creates a matrix from a given angle around a given axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotate(dest, dest, rad, axis); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @param {ReadonlyVec3} axis the axis to rotate around + * @returns {mat4} out + */ + + function fromRotation$1(out, rad, axis) { + var x = axis[0], + y = axis[1], + z = axis[2]; + var len = Math.hypot(x, y, z); + var s, c, t; + + if (len < EPSILON) { + return null; + } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; // Perform rotation-specific matrix multiplication + + out[0] = x * x * t + c; + out[1] = y * x * t + z * s; + out[2] = z * x * t - y * s; + out[3] = 0; + out[4] = x * y * t - z * s; + out[5] = y * y * t + c; + out[6] = z * y * t + x * s; + out[7] = 0; + out[8] = x * z * t + y * s; + out[9] = y * z * t - x * s; + out[10] = z * z * t + c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Creates a matrix from the given angle around the X axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateX(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function fromXRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); // Perform axis-specific matrix multiplication + + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = c; + out[6] = s; + out[7] = 0; + out[8] = 0; + out[9] = -s; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Creates a matrix from the given angle around the Y axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateY(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function fromYRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); // Perform axis-specific matrix multiplication + + out[0] = c; + out[1] = 0; + out[2] = -s; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = s; + out[9] = 0; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Creates a matrix from the given angle around the Z axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.rotateZ(dest, dest, rad); + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + + function fromZRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); // Perform axis-specific matrix multiplication + + out[0] = c; + out[1] = s; + out[2] = 0; + out[3] = 0; + out[4] = -s; + out[5] = c; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * let quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {ReadonlyVec3} v Translation vector + * @returns {mat4} out + */ + + function fromRotationTranslation$1(out, q, v) { + // Quaternion math + var x = q[0], + y = q[1], + z = q[2], + w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + /** + * Creates a new mat4 from a dual quat. + * + * @param {mat4} out Matrix + * @param {ReadonlyQuat2} a Dual Quaternion + * @returns {mat4} mat4 receiving operation result + */ + + function fromQuat2(out, a) { + var translation = new ARRAY_TYPE(3); + var bx = -a[0], + by = -a[1], + bz = -a[2], + bw = a[3], + ax = a[4], + ay = a[5], + az = a[6], + aw = a[7]; + var magnitude = bx * bx + by * by + bz * bz + bw * bw; //Only scale if it makes sense + + if (magnitude > 0) { + translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude; + translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude; + translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude; + } else { + translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2; + translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2; + translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2; + } + + fromRotationTranslation$1(out, a, translation); + return out; + } + /** + * Returns the translation vector component of a transformation + * matrix. If a matrix is built with fromRotationTranslation, + * the returned vector will be the same as the translation vector + * originally supplied. + * @param {vec3} out Vector to receive translation component + * @param {ReadonlyMat4} mat Matrix to be decomposed (input) + * @return {vec3} out + */ + + function getTranslation$1(out, mat) { + out[0] = mat[12]; + out[1] = mat[13]; + out[2] = mat[14]; + return out; + } + /** + * Returns the scaling factor component of a transformation + * matrix. If a matrix is built with fromRotationTranslationScale + * with a normalized Quaternion paramter, the returned vector will be + * the same as the scaling vector + * originally supplied. + * @param {vec3} out Vector to receive scaling factor component + * @param {ReadonlyMat4} mat Matrix to be decomposed (input) + * @return {vec3} out + */ + + function getScaling(out, mat) { + var m11 = mat[0]; + var m12 = mat[1]; + var m13 = mat[2]; + var m21 = mat[4]; + var m22 = mat[5]; + var m23 = mat[6]; + var m31 = mat[8]; + var m32 = mat[9]; + var m33 = mat[10]; + out[0] = Math.hypot(m11, m12, m13); + out[1] = Math.hypot(m21, m22, m23); + out[2] = Math.hypot(m31, m32, m33); + return out; + } + /** + * Returns a quaternion representing the rotational component + * of a transformation matrix. If a matrix is built with + * fromRotationTranslation, the returned quaternion will be the + * same as the quaternion originally supplied. + * @param {quat} out Quaternion to receive the rotation component + * @param {ReadonlyMat4} mat Matrix to be decomposed (input) + * @return {quat} out + */ + + function getRotation(out, mat) { + var scaling = new ARRAY_TYPE(3); + getScaling(scaling, mat); + var is1 = 1 / scaling[0]; + var is2 = 1 / scaling[1]; + var is3 = 1 / scaling[2]; + var sm11 = mat[0] * is1; + var sm12 = mat[1] * is2; + var sm13 = mat[2] * is3; + var sm21 = mat[4] * is1; + var sm22 = mat[5] * is2; + var sm23 = mat[6] * is3; + var sm31 = mat[8] * is1; + var sm32 = mat[9] * is2; + var sm33 = mat[10] * is3; + var trace = sm11 + sm22 + sm33; + var S = 0; + + if (trace > 0) { + S = Math.sqrt(trace + 1.0) * 2; + out[3] = 0.25 * S; + out[0] = (sm23 - sm32) / S; + out[1] = (sm31 - sm13) / S; + out[2] = (sm12 - sm21) / S; + } else if (sm11 > sm22 && sm11 > sm33) { + S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2; + out[3] = (sm23 - sm32) / S; + out[0] = 0.25 * S; + out[1] = (sm12 + sm21) / S; + out[2] = (sm31 + sm13) / S; + } else if (sm22 > sm33) { + S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2; + out[3] = (sm31 - sm13) / S; + out[0] = (sm12 + sm21) / S; + out[1] = 0.25 * S; + out[2] = (sm23 + sm32) / S; + } else { + S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2; + out[3] = (sm12 - sm21) / S; + out[0] = (sm31 + sm13) / S; + out[1] = (sm23 + sm32) / S; + out[2] = 0.25 * S; + } + + return out; + } + /** + * Decomposes a transformation matrix into its rotation, translation + * and scale components. Returns only the rotation component + * @param {quat} out_r Quaternion to receive the rotation component + * @param {vec3} out_t Vector to receive the translation vector + * @param {vec3} out_s Vector to receive the scaling factor + * @param {ReadonlyMat4} mat Matrix to be decomposed (input) + * @returns {quat} out_r + */ + + function decompose(out_r, out_t, out_s, mat) { + out_t[0] = mat[12]; + out_t[1] = mat[13]; + out_t[2] = mat[14]; + var m11 = mat[0]; + var m12 = mat[1]; + var m13 = mat[2]; + var m21 = mat[4]; + var m22 = mat[5]; + var m23 = mat[6]; + var m31 = mat[8]; + var m32 = mat[9]; + var m33 = mat[10]; + out_s[0] = Math.hypot(m11, m12, m13); + out_s[1] = Math.hypot(m21, m22, m23); + out_s[2] = Math.hypot(m31, m32, m33); + var is1 = 1 / out_s[0]; + var is2 = 1 / out_s[1]; + var is3 = 1 / out_s[2]; + var sm11 = m11 * is1; + var sm12 = m12 * is2; + var sm13 = m13 * is3; + var sm21 = m21 * is1; + var sm22 = m22 * is2; + var sm23 = m23 * is3; + var sm31 = m31 * is1; + var sm32 = m32 * is2; + var sm33 = m33 * is3; + var trace = sm11 + sm22 + sm33; + var S = 0; + + if (trace > 0) { + S = Math.sqrt(trace + 1.0) * 2; + out_r[3] = 0.25 * S; + out_r[0] = (sm23 - sm32) / S; + out_r[1] = (sm31 - sm13) / S; + out_r[2] = (sm12 - sm21) / S; + } else if (sm11 > sm22 && sm11 > sm33) { + S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2; + out_r[3] = (sm23 - sm32) / S; + out_r[0] = 0.25 * S; + out_r[1] = (sm12 + sm21) / S; + out_r[2] = (sm31 + sm13) / S; + } else if (sm22 > sm33) { + S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2; + out_r[3] = (sm31 - sm13) / S; + out_r[0] = (sm12 + sm21) / S; + out_r[1] = 0.25 * S; + out_r[2] = (sm23 + sm32) / S; + } else { + S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2; + out_r[3] = (sm12 - sm21) / S; + out_r[0] = (sm31 + sm13) / S; + out_r[1] = (sm23 + sm32) / S; + out_r[2] = 0.25 * S; + } + + return out_r; + } + /** + * Creates a matrix from a quaternion rotation, vector translation and vector scale + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * let quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * mat4.scale(dest, scale) + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {ReadonlyVec3} v Translation vector + * @param {ReadonlyVec3} s Scaling vector + * @returns {mat4} out + */ + + function fromRotationTranslationScale(out, q, v, s) { + // Quaternion math + var x = q[0], + y = q[1], + z = q[2], + w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + var sx = s[0]; + var sy = s[1]; + var sz = s[2]; + out[0] = (1 - (yy + zz)) * sx; + out[1] = (xy + wz) * sx; + out[2] = (xz - wy) * sx; + out[3] = 0; + out[4] = (xy - wz) * sy; + out[5] = (1 - (xx + zz)) * sy; + out[6] = (yz + wx) * sy; + out[7] = 0; + out[8] = (xz + wy) * sz; + out[9] = (yz - wx) * sz; + out[10] = (1 - (xx + yy)) * sz; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; + } + /** + * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * mat4.translate(dest, origin); + * let quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * mat4.scale(dest, scale) + * mat4.translate(dest, negativeOrigin); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {ReadonlyVec3} v Translation vector + * @param {ReadonlyVec3} s Scaling vector + * @param {ReadonlyVec3} o The origin vector around which to scale and rotate + * @returns {mat4} out + */ + + function fromRotationTranslationScaleOrigin(out, q, v, s, o) { + // Quaternion math + var x = q[0], + y = q[1], + z = q[2], + w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + var sx = s[0]; + var sy = s[1]; + var sz = s[2]; + var ox = o[0]; + var oy = o[1]; + var oz = o[2]; + var out0 = (1 - (yy + zz)) * sx; + var out1 = (xy + wz) * sx; + var out2 = (xz - wy) * sx; + var out4 = (xy - wz) * sy; + var out5 = (1 - (xx + zz)) * sy; + var out6 = (yz + wx) * sy; + var out8 = (xz + wy) * sz; + var out9 = (yz - wx) * sz; + var out10 = (1 - (xx + yy)) * sz; + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = 0; + out[4] = out4; + out[5] = out5; + out[6] = out6; + out[7] = 0; + out[8] = out8; + out[9] = out9; + out[10] = out10; + out[11] = 0; + out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz); + out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz); + out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz); + out[15] = 1; + return out; + } + /** + * Calculates a 4x4 matrix from the given quaternion + * + * @param {mat4} out mat4 receiving operation result + * @param {ReadonlyQuat} q Quaternion to create matrix from + * + * @returns {mat4} out + */ + + function fromQuat(out, q) { + var x = q[0], + y = q[1], + z = q[2], + w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var yx = y * x2; + var yy = y * y2; + var zx = z * x2; + var zy = z * y2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + out[0] = 1 - yy - zz; + out[1] = yx + wz; + out[2] = zx - wy; + out[3] = 0; + out[4] = yx - wz; + out[5] = 1 - xx - zz; + out[6] = zy + wx; + out[7] = 0; + out[8] = zx + wy; + out[9] = zy - wx; + out[10] = 1 - xx - yy; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + } + /** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ + + function frustum(out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left); + var tb = 1 / (top - bottom); + var nf = 1 / (near - far); + out[0] = near * 2 * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = near * 2 * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = far * near * 2 * nf; + out[15] = 0; + return out; + } + /** + * Generates a perspective projection matrix with the given bounds. + * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1], + * which matches WebGL/OpenGL's clip volume. + * Passing null/undefined/no value for far will generate infinite projection matrix. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum, can be null or Infinity + * @returns {mat4} out + */ + + function perspectiveNO(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + + if (far != null && far !== Infinity) { + var nf = 1 / (near - far); + out[10] = (far + near) * nf; + out[14] = 2 * far * near * nf; + } else { + out[10] = -1; + out[14] = -2 * near; + } + + return out; + } + /** + * Alias for {@link mat4.perspectiveNO} + * @function + */ + + var perspective = perspectiveNO; + /** + * Generates a perspective projection matrix suitable for WebGPU with the given bounds. + * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1], + * which matches WebGPU/Vulkan/DirectX/Metal's clip volume. + * Passing null/undefined/no value for far will generate infinite projection matrix. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum, can be null or Infinity + * @returns {mat4} out + */ + + function perspectiveZO(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + + if (far != null && far !== Infinity) { + var nf = 1 / (near - far); + out[10] = far * nf; + out[14] = far * near * nf; + } else { + out[10] = -1; + out[14] = -near; + } + + return out; + } + /** + * Generates a perspective projection matrix with the given field of view. + * This is primarily useful for generating projection matrices to be used + * with the still experiemental WebVR API. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + + function perspectiveFromFieldOfView(out, fov, near, far) { + var upTan = Math.tan(fov.upDegrees * Math.PI / 180.0); + var downTan = Math.tan(fov.downDegrees * Math.PI / 180.0); + var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0); + var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0); + var xScale = 2.0 / (leftTan + rightTan); + var yScale = 2.0 / (upTan + downTan); + out[0] = xScale; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + out[4] = 0.0; + out[5] = yScale; + out[6] = 0.0; + out[7] = 0.0; + out[8] = -((leftTan - rightTan) * xScale * 0.5); + out[9] = (upTan - downTan) * yScale * 0.5; + out[10] = far / (near - far); + out[11] = -1.0; + out[12] = 0.0; + out[13] = 0.0; + out[14] = far * near / (near - far); + out[15] = 0.0; + return out; + } + /** + * Generates a orthogonal projection matrix with the given bounds. + * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1], + * which matches WebGL/OpenGL's clip volume. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + + function orthoNO(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right); + var bt = 1 / (bottom - top); + var nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; + } + /** + * Alias for {@link mat4.orthoNO} + * @function + */ + + var ortho = orthoNO; + /** + * Generates a orthogonal projection matrix with the given bounds. + * The near/far clip planes correspond to a normalized device coordinate Z range of [0, 1], + * which matches WebGPU/Vulkan/DirectX/Metal's clip volume. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + + function orthoZO(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right); + var bt = 1 / (bottom - top); + var nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = near * nf; + out[15] = 1; + return out; + } + /** + * Generates a look-at matrix with the given eye position, focal point, and up axis. + * If you want a matrix that actually makes an object look at another object, you should use targetTo instead. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {ReadonlyVec3} eye Position of the viewer + * @param {ReadonlyVec3} center Point the viewer is looking at + * @param {ReadonlyVec3} up vec3 pointing up + * @returns {mat4} out + */ + + function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len; + var eyex = eye[0]; + var eyey = eye[1]; + var eyez = eye[2]; + var upx = up[0]; + var upy = up[1]; + var upz = up[2]; + var centerx = center[0]; + var centery = center[1]; + var centerz = center[2]; + + if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) { + return identity$2(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + len = 1 / Math.hypot(z0, z1, z2); + z0 *= len; + z1 *= len; + z2 *= len; + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.hypot(x0, x1, x2); + + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + len = Math.hypot(y0, y1, y2); + + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + return out; + } + /** + * Generates a matrix that makes something look at something else. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {ReadonlyVec3} eye Position of the viewer + * @param {ReadonlyVec3} center Point the viewer is looking at + * @param {ReadonlyVec3} up vec3 pointing up + * @returns {mat4} out + */ + + function targetTo(out, eye, target, up) { + var eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2]; + var z0 = eyex - target[0], + z1 = eyey - target[1], + z2 = eyez - target[2]; + var len = z0 * z0 + z1 * z1 + z2 * z2; + + if (len > 0) { + len = 1 / Math.sqrt(len); + z0 *= len; + z1 *= len; + z2 *= len; + } + + var x0 = upy * z2 - upz * z1, + x1 = upz * z0 - upx * z2, + x2 = upx * z1 - upy * z0; + len = x0 * x0 + x1 * x1 + x2 * x2; + + if (len > 0) { + len = 1 / Math.sqrt(len); + x0 *= len; + x1 *= len; + x2 *= len; + } + + out[0] = x0; + out[1] = x1; + out[2] = x2; + out[3] = 0; + out[4] = z1 * x2 - z2 * x1; + out[5] = z2 * x0 - z0 * x2; + out[6] = z0 * x1 - z1 * x0; + out[7] = 0; + out[8] = z0; + out[9] = z1; + out[10] = z2; + out[11] = 0; + out[12] = eyex; + out[13] = eyey; + out[14] = eyez; + out[15] = 1; + return out; + } + /** + * Returns a string representation of a mat4 + * + * @param {ReadonlyMat4} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + + function str$5(a) { + return "mat4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ", " + a[9] + ", " + a[10] + ", " + a[11] + ", " + a[12] + ", " + a[13] + ", " + a[14] + ", " + a[15] + ")"; + } + /** + * Returns Frobenius norm of a mat4 + * + * @param {ReadonlyMat4} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + + function frob(a) { + return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); + } + /** + * Adds two mat4's + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the first operand + * @param {ReadonlyMat4} b the second operand + * @returns {mat4} out + */ + + function add$5(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + out[8] = a[8] + b[8]; + out[9] = a[9] + b[9]; + out[10] = a[10] + b[10]; + out[11] = a[11] + b[11]; + out[12] = a[12] + b[12]; + out[13] = a[13] + b[13]; + out[14] = a[14] + b[14]; + out[15] = a[15] + b[15]; + return out; + } + /** + * Subtracts matrix b from matrix a + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the first operand + * @param {ReadonlyMat4} b the second operand + * @returns {mat4} out + */ + + function subtract$3(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + out[4] = a[4] - b[4]; + out[5] = a[5] - b[5]; + out[6] = a[6] - b[6]; + out[7] = a[7] - b[7]; + out[8] = a[8] - b[8]; + out[9] = a[9] - b[9]; + out[10] = a[10] - b[10]; + out[11] = a[11] - b[11]; + out[12] = a[12] - b[12]; + out[13] = a[13] - b[13]; + out[14] = a[14] - b[14]; + out[15] = a[15] - b[15]; + return out; + } + /** + * Multiply each element of the matrix by a scalar. + * + * @param {mat4} out the receiving matrix + * @param {ReadonlyMat4} a the matrix to scale + * @param {Number} b amount to scale the matrix's elements by + * @returns {mat4} out + */ + + function multiplyScalar(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + out[6] = a[6] * b; + out[7] = a[7] * b; + out[8] = a[8] * b; + out[9] = a[9] * b; + out[10] = a[10] * b; + out[11] = a[11] * b; + out[12] = a[12] * b; + out[13] = a[13] * b; + out[14] = a[14] * b; + out[15] = a[15] * b; + return out; + } + /** + * Adds two mat4's after multiplying each element of the second operand by a scalar value. + * + * @param {mat4} out the receiving vector + * @param {ReadonlyMat4} a the first operand + * @param {ReadonlyMat4} b the second operand + * @param {Number} scale the amount to scale b's elements by before adding + * @returns {mat4} out + */ + + function multiplyScalarAndAdd(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + out[3] = a[3] + b[3] * scale; + out[4] = a[4] + b[4] * scale; + out[5] = a[5] + b[5] * scale; + out[6] = a[6] + b[6] * scale; + out[7] = a[7] + b[7] * scale; + out[8] = a[8] + b[8] * scale; + out[9] = a[9] + b[9] * scale; + out[10] = a[10] + b[10] * scale; + out[11] = a[11] + b[11] * scale; + out[12] = a[12] + b[12] * scale; + out[13] = a[13] + b[13] * scale; + out[14] = a[14] + b[14] * scale; + out[15] = a[15] + b[15] * scale; + return out; + } + /** + * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyMat4} a The first matrix. + * @param {ReadonlyMat4} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function exactEquals$5(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15]; + } + /** + * Returns whether or not the matrices have approximately the same elements in the same position. + * + * @param {ReadonlyMat4} a The first matrix. + * @param {ReadonlyMat4} b The second matrix. + * @returns {Boolean} True if the matrices are equal, false otherwise. + */ + + function equals$5(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var a4 = a[4], + a5 = a[5], + a6 = a[6], + a7 = a[7]; + var a8 = a[8], + a9 = a[9], + a10 = a[10], + a11 = a[11]; + var a12 = a[12], + a13 = a[13], + a14 = a[14], + a15 = a[15]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3]; + var b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7]; + var b8 = b[8], + b9 = b[9], + b10 = b[10], + b11 = b[11]; + var b12 = b[12], + b13 = b[13], + b14 = b[14], + b15 = b[15]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= EPSILON * Math.max(1.0, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= EPSILON * Math.max(1.0, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= EPSILON * Math.max(1.0, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= EPSILON * Math.max(1.0, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= EPSILON * Math.max(1.0, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= EPSILON * Math.max(1.0, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= EPSILON * Math.max(1.0, Math.abs(a15), Math.abs(b15)); + } + /** + * Alias for {@link mat4.multiply} + * @function + */ + + var mul$5 = multiply$5; + /** + * Alias for {@link mat4.subtract} + * @function + */ + + var sub$3 = subtract$3; + + var mat4 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$5, + clone: clone$5, + copy: copy$5, + fromValues: fromValues$5, + set: set$5, + identity: identity$2, + transpose: transpose, + invert: invert$2, + adjoint: adjoint, + determinant: determinant, + multiply: multiply$5, + translate: translate$1, + scale: scale$5, + rotate: rotate$1, + rotateX: rotateX$3, + rotateY: rotateY$3, + rotateZ: rotateZ$3, + fromTranslation: fromTranslation$1, + fromScaling: fromScaling, + fromRotation: fromRotation$1, + fromXRotation: fromXRotation, + fromYRotation: fromYRotation, + fromZRotation: fromZRotation, + fromRotationTranslation: fromRotationTranslation$1, + fromQuat2: fromQuat2, + getTranslation: getTranslation$1, + getScaling: getScaling, + getRotation: getRotation, + decompose: decompose, + fromRotationTranslationScale: fromRotationTranslationScale, + fromRotationTranslationScaleOrigin: fromRotationTranslationScaleOrigin, + fromQuat: fromQuat, + frustum: frustum, + perspectiveNO: perspectiveNO, + perspective: perspective, + perspectiveZO: perspectiveZO, + perspectiveFromFieldOfView: perspectiveFromFieldOfView, + orthoNO: orthoNO, + ortho: ortho, + orthoZO: orthoZO, + lookAt: lookAt, + targetTo: targetTo, + str: str$5, + frob: frob, + add: add$5, + subtract: subtract$3, + multiplyScalar: multiplyScalar, + multiplyScalarAndAdd: multiplyScalarAndAdd, + exactEquals: exactEquals$5, + equals: equals$5, + mul: mul$5, + sub: sub$3 + }); + + /** + * 3 Dimensional Vector + * @module vec3 + */ + + /** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ + + function create$4() { + var out = new ARRAY_TYPE(3); + + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + + return out; + } + /** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {ReadonlyVec3} a vector to clone + * @returns {vec3} a new 3D vector + */ + + function clone$4(a) { + var out = new ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + } + /** + * Calculates the length of a vec3 + * + * @param {ReadonlyVec3} a vector to calculate length of + * @returns {Number} length of a + */ + + function length$4(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return Math.hypot(x, y, z); + } + /** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ + + function fromValues$4(x, y, z) { + var out = new ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; + } + /** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the source vector + * @returns {vec3} out + */ + + function copy$4(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + } + /** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ + + function set$4(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; + } + /** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function add$4(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; + } + /** + * Subtracts vector b from vector a + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function subtract$2(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; + } + /** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function multiply$4(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; + } + /** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function divide$2(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; + } + /** + * Math.ceil the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to ceil + * @returns {vec3} out + */ + + function ceil$2(out, a) { + out[0] = Math.ceil(a[0]); + out[1] = Math.ceil(a[1]); + out[2] = Math.ceil(a[2]); + return out; + } + /** + * Math.floor the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to floor + * @returns {vec3} out + */ + + function floor$2(out, a) { + out[0] = Math.floor(a[0]); + out[1] = Math.floor(a[1]); + out[2] = Math.floor(a[2]); + return out; + } + /** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function min$2(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; + } + /** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function max$2(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; + } + /** + * Math.round the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to round + * @returns {vec3} out + */ + + function round$2(out, a) { + out[0] = Math.round(a[0]); + out[1] = Math.round(a[1]); + out[2] = Math.round(a[2]); + return out; + } + /** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec3} out + */ + + function scale$4(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; + } + /** + * Adds two vec3's after scaling the second operand by a scalar value + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec3} out + */ + + function scaleAndAdd$2(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + return out; + } + /** + * Calculates the euclidian distance between two vec3's + * + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {Number} distance between a and b + */ + + function distance$2(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + return Math.hypot(x, y, z); + } + /** + * Calculates the squared euclidian distance between two vec3's + * + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {Number} squared distance between a and b + */ + + function squaredDistance$2(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + return x * x + y * y + z * z; + } + /** + * Calculates the squared length of a vec3 + * + * @param {ReadonlyVec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + + function squaredLength$4(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return x * x + y * y + z * z; + } + /** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to negate + * @returns {vec3} out + */ + + function negate$2(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; + } + /** + * Returns the inverse of the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to invert + * @returns {vec3} out + */ + + function inverse$2(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + return out; + } + /** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a vector to normalize + * @returns {vec3} out + */ + + function normalize$4(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var len = x * x + y * y + z * z; + + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + } + + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + return out; + } + /** + * Calculates the dot product of two vec3's + * + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {Number} dot product of a and b + */ + + function dot$4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + /** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @returns {vec3} out + */ + + function cross$2(out, a, b) { + var ax = a[0], + ay = a[1], + az = a[2]; + var bx = b[0], + by = b[1], + bz = b[2]; + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; + } + /** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec3} out + */ + + function lerp$4(out, a, b, t) { + var ax = a[0]; + var ay = a[1]; + var az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; + } + /** + * Performs a spherical linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec3} out + */ + + function slerp$1(out, a, b, t) { + var angle = Math.acos(Math.min(Math.max(dot$4(a, b), -1), 1)); + var sinTotal = Math.sin(angle); + var ratioA = Math.sin((1 - t) * angle) / sinTotal; + var ratioB = Math.sin(t * angle) / sinTotal; + out[0] = ratioA * a[0] + ratioB * b[0]; + out[1] = ratioA * a[1] + ratioB * b[1]; + out[2] = ratioA * a[2] + ratioB * b[2]; + return out; + } + /** + * Performs a hermite interpolation with two control points + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @param {ReadonlyVec3} c the third operand + * @param {ReadonlyVec3} d the fourth operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec3} out + */ + + function hermite(out, a, b, c, d, t) { + var factorTimes2 = t * t; + var factor1 = factorTimes2 * (2 * t - 3) + 1; + var factor2 = factorTimes2 * (t - 2) + t; + var factor3 = factorTimes2 * (t - 1); + var factor4 = factorTimes2 * (3 - 2 * t); + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + return out; + } + /** + * Performs a bezier interpolation with two control points + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the first operand + * @param {ReadonlyVec3} b the second operand + * @param {ReadonlyVec3} c the third operand + * @param {ReadonlyVec3} d the fourth operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec3} out + */ + + function bezier(out, a, b, c, d, t) { + var inverseFactor = 1 - t; + var inverseFactorTimesTwo = inverseFactor * inverseFactor; + var factorTimes2 = t * t; + var factor1 = inverseFactorTimesTwo * inverseFactor; + var factor2 = 3 * t * inverseFactorTimesTwo; + var factor3 = 3 * factorTimes2 * inverseFactor; + var factor4 = factorTimes2 * t; + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + return out; + } + /** + * Generates a random vector with the given scale + * + * @param {vec3} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned + * @returns {vec3} out + */ + + function random$3(out, scale) { + scale = scale || 1.0; + var r = RANDOM() * 2.0 * Math.PI; + var z = RANDOM() * 2.0 - 1.0; + var zScale = Math.sqrt(1.0 - z * z) * scale; + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; + } + /** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the vector to transform + * @param {ReadonlyMat4} m matrix to transform with + * @returns {vec3} out + */ + + function transformMat4$2(out, a, m) { + var x = a[0], + y = a[1], + z = a[2]; + var w = m[3] * x + m[7] * y + m[11] * z + m[15]; + w = w || 1.0; + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + return out; + } + /** + * Transforms the vec3 with a mat3. + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the vector to transform + * @param {ReadonlyMat3} m the 3x3 matrix to transform with + * @returns {vec3} out + */ + + function transformMat3$1(out, a, m) { + var x = a[0], + y = a[1], + z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; + } + /** + * Transforms the vec3 with a quat + * Can also be used for dual quaternions. (Multiply it with the real part) + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec3} a the vector to transform + * @param {ReadonlyQuat} q quaternion to transform with + * @returns {vec3} out + */ + + function transformQuat$1(out, a, q) { + // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed + var qx = q[0], + qy = q[1], + qz = q[2], + qw = q[3]; + var x = a[0], + y = a[1], + z = a[2]; // var qvec = [qx, qy, qz]; + // var uv = vec3.cross([], qvec, a); + + var uvx = qy * z - qz * y, + uvy = qz * x - qx * z, + uvz = qx * y - qy * x; // var uuv = vec3.cross([], qvec, uv); + + var uuvx = qy * uvz - qz * uvy, + uuvy = qz * uvx - qx * uvz, + uuvz = qx * uvy - qy * uvx; // vec3.scale(uv, uv, 2 * w); + + var w2 = qw * 2; + uvx *= w2; + uvy *= w2; + uvz *= w2; // vec3.scale(uuv, uuv, 2); + + uuvx *= 2; + uuvy *= 2; + uuvz *= 2; // return vec3.add(out, a, vec3.add(out, uv, uuv)); + + out[0] = x + uvx + uuvx; + out[1] = y + uvy + uuvy; + out[2] = z + uvz + uuvz; + return out; + } + /** + * Rotate a 3D vector around the x-axis + * @param {vec3} out The receiving vec3 + * @param {ReadonlyVec3} a The vec3 point to rotate + * @param {ReadonlyVec3} b The origin of the rotation + * @param {Number} rad The angle of rotation in radians + * @returns {vec3} out + */ + + function rotateX$2(out, a, b, rad) { + var p = [], + r = []; //Translate point to the origin + + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; //perform rotation + + r[0] = p[0]; + r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad); + r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); //translate to correct position + + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; + } + /** + * Rotate a 3D vector around the y-axis + * @param {vec3} out The receiving vec3 + * @param {ReadonlyVec3} a The vec3 point to rotate + * @param {ReadonlyVec3} b The origin of the rotation + * @param {Number} rad The angle of rotation in radians + * @returns {vec3} out + */ + + function rotateY$2(out, a, b, rad) { + var p = [], + r = []; //Translate point to the origin + + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; //perform rotation + + r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad); + r[1] = p[1]; + r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); //translate to correct position + + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; + } + /** + * Rotate a 3D vector around the z-axis + * @param {vec3} out The receiving vec3 + * @param {ReadonlyVec3} a The vec3 point to rotate + * @param {ReadonlyVec3} b The origin of the rotation + * @param {Number} rad The angle of rotation in radians + * @returns {vec3} out + */ + + function rotateZ$2(out, a, b, rad) { + var p = [], + r = []; //Translate point to the origin + + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; //perform rotation + + r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad); + r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad); + r[2] = p[2]; //translate to correct position + + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; + } + /** + * Get the angle between two 3D vectors + * @param {ReadonlyVec3} a The first operand + * @param {ReadonlyVec3} b The second operand + * @returns {Number} The angle in radians + */ + + function angle$1(a, b) { + var ax = a[0], + ay = a[1], + az = a[2], + bx = b[0], + by = b[1], + bz = b[2], + mag = Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz)), + cosine = mag && dot$4(a, b) / mag; + return Math.acos(Math.min(Math.max(cosine, -1), 1)); + } + /** + * Set the components of a vec3 to zero + * + * @param {vec3} out the receiving vector + * @returns {vec3} out + */ + + function zero$2(out) { + out[0] = 0.0; + out[1] = 0.0; + out[2] = 0.0; + return out; + } + /** + * Returns a string representation of a vector + * + * @param {ReadonlyVec3} a vector to represent as a string + * @returns {String} string representation of the vector + */ + + function str$4(a) { + return "vec3(" + a[0] + ", " + a[1] + ", " + a[2] + ")"; + } + /** + * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyVec3} a The first vector. + * @param {ReadonlyVec3} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function exactEquals$4(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]; + } + /** + * Returns whether or not the vectors have approximately the same elements in the same position. + * + * @param {ReadonlyVec3} a The first vector. + * @param {ReadonlyVec3} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function equals$4(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2]; + var b0 = b[0], + b1 = b[1], + b2 = b[2]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)); + } + /** + * Alias for {@link vec3.subtract} + * @function + */ + + var sub$2 = subtract$2; + /** + * Alias for {@link vec3.multiply} + * @function + */ + + var mul$4 = multiply$4; + /** + * Alias for {@link vec3.divide} + * @function + */ + + var div$2 = divide$2; + /** + * Alias for {@link vec3.distance} + * @function + */ + + var dist$2 = distance$2; + /** + * Alias for {@link vec3.squaredDistance} + * @function + */ + + var sqrDist$2 = squaredDistance$2; + /** + * Alias for {@link vec3.length} + * @function + */ + + var len$4 = length$4; + /** + * Alias for {@link vec3.squaredLength} + * @function + */ + + var sqrLen$4 = squaredLength$4; + /** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + + var forEach$2 = function () { + var vec = create$4(); + return function (a, stride, offset, count, fn, arg) { + var i, l; + + if (!stride) { + stride = 3; + } + + if (!offset) { + offset = 0; + } + + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + + for (i = offset; i < l; i += stride) { + vec[0] = a[i]; + vec[1] = a[i + 1]; + vec[2] = a[i + 2]; + fn(vec, vec, arg); + a[i] = vec[0]; + a[i + 1] = vec[1]; + a[i + 2] = vec[2]; + } + + return a; + }; + }(); + + var vec3 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$4, + clone: clone$4, + length: length$4, + fromValues: fromValues$4, + copy: copy$4, + set: set$4, + add: add$4, + subtract: subtract$2, + multiply: multiply$4, + divide: divide$2, + ceil: ceil$2, + floor: floor$2, + min: min$2, + max: max$2, + round: round$2, + scale: scale$4, + scaleAndAdd: scaleAndAdd$2, + distance: distance$2, + squaredDistance: squaredDistance$2, + squaredLength: squaredLength$4, + negate: negate$2, + inverse: inverse$2, + normalize: normalize$4, + dot: dot$4, + cross: cross$2, + lerp: lerp$4, + slerp: slerp$1, + hermite: hermite, + bezier: bezier, + random: random$3, + transformMat4: transformMat4$2, + transformMat3: transformMat3$1, + transformQuat: transformQuat$1, + rotateX: rotateX$2, + rotateY: rotateY$2, + rotateZ: rotateZ$2, + angle: angle$1, + zero: zero$2, + str: str$4, + exactEquals: exactEquals$4, + equals: equals$4, + sub: sub$2, + mul: mul$4, + div: div$2, + dist: dist$2, + sqrDist: sqrDist$2, + len: len$4, + sqrLen: sqrLen$4, + forEach: forEach$2 + }); + + /** + * 4 Dimensional Vector + * @module vec4 + */ + + /** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ + + function create$3() { + var out = new ARRAY_TYPE(4); + + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + + return out; + } + /** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {ReadonlyVec4} a vector to clone + * @returns {vec4} a new 4D vector + */ + + function clone$3(a) { + var out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + /** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ + + function fromValues$3(x, y, z, w) { + var out = new ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + } + /** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the source vector + * @returns {vec4} out + */ + + function copy$3(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + /** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ + + function set$3(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + } + /** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function add$3(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; + } + /** + * Subtracts vector b from vector a + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function subtract$1(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; + } + /** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function multiply$3(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; + } + /** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function divide$1(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; + } + /** + * Math.ceil the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to ceil + * @returns {vec4} out + */ + + function ceil$1(out, a) { + out[0] = Math.ceil(a[0]); + out[1] = Math.ceil(a[1]); + out[2] = Math.ceil(a[2]); + out[3] = Math.ceil(a[3]); + return out; + } + /** + * Math.floor the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to floor + * @returns {vec4} out + */ + + function floor$1(out, a) { + out[0] = Math.floor(a[0]); + out[1] = Math.floor(a[1]); + out[2] = Math.floor(a[2]); + out[3] = Math.floor(a[3]); + return out; + } + /** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function min$1(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; + } + /** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {vec4} out + */ + + function max$1(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; + } + /** + * Math.round the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to round + * @returns {vec4} out + */ + + function round$1(out, a) { + out[0] = Math.round(a[0]); + out[1] = Math.round(a[1]); + out[2] = Math.round(a[2]); + out[3] = Math.round(a[3]); + return out; + } + /** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec4} out + */ + + function scale$3(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; + } + /** + * Adds two vec4's after scaling the second operand by a scalar value + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec4} out + */ + + function scaleAndAdd$1(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + out[2] = a[2] + b[2] * scale; + out[3] = a[3] + b[3] * scale; + return out; + } + /** + * Calculates the euclidian distance between two vec4's + * + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {Number} distance between a and b + */ + + function distance$1(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + var w = b[3] - a[3]; + return Math.hypot(x, y, z, w); + } + /** + * Calculates the squared euclidian distance between two vec4's + * + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {Number} squared distance between a and b + */ + + function squaredDistance$1(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + var w = b[3] - a[3]; + return x * x + y * y + z * z + w * w; + } + /** + * Calculates the length of a vec4 + * + * @param {ReadonlyVec4} a vector to calculate length of + * @returns {Number} length of a + */ + + function length$3(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + return Math.hypot(x, y, z, w); + } + /** + * Calculates the squared length of a vec4 + * + * @param {ReadonlyVec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + + function squaredLength$3(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + return x * x + y * y + z * z + w * w; + } + /** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to negate + * @returns {vec4} out + */ + + function negate$1(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; + } + /** + * Returns the inverse of the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to invert + * @returns {vec4} out + */ + + function inverse$1(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + out[3] = 1.0 / a[3]; + return out; + } + /** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a vector to normalize + * @returns {vec4} out + */ + + function normalize$3(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + var len = x * x + y * y + z * z + w * w; + + if (len > 0) { + len = 1 / Math.sqrt(len); + } + + out[0] = x * len; + out[1] = y * len; + out[2] = z * len; + out[3] = w * len; + return out; + } + /** + * Calculates the dot product of two vec4's + * + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @returns {Number} dot product of a and b + */ + + function dot$3(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + /** + * Returns the cross-product of three vectors in a 4-dimensional space + * + * @param {ReadonlyVec4} result the receiving vector + * @param {ReadonlyVec4} U the first vector + * @param {ReadonlyVec4} V the second vector + * @param {ReadonlyVec4} W the third vector + * @returns {vec4} result + */ + + function cross$1(out, u, v, w) { + var A = v[0] * w[1] - v[1] * w[0], + B = v[0] * w[2] - v[2] * w[0], + C = v[0] * w[3] - v[3] * w[0], + D = v[1] * w[2] - v[2] * w[1], + E = v[1] * w[3] - v[3] * w[1], + F = v[2] * w[3] - v[3] * w[2]; + var G = u[0]; + var H = u[1]; + var I = u[2]; + var J = u[3]; + out[0] = H * F - I * E + J * D; + out[1] = -(G * F) + I * C - J * B; + out[2] = G * E - H * C + J * A; + out[3] = -(G * D) + H * B - I * A; + return out; + } + /** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the first operand + * @param {ReadonlyVec4} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec4} out + */ + + function lerp$3(out, a, b, t) { + var ax = a[0]; + var ay = a[1]; + var az = a[2]; + var aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; + } + /** + * Generates a random vector with the given scale + * + * @param {vec4} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned + * @returns {vec4} out + */ + + function random$2(out, scale) { + scale = scale || 1.0; // Marsaglia, George. Choosing a Point from the Surface of a + // Sphere. Ann. Math. Statist. 43 (1972), no. 2, 645--646. + // http://projecteuclid.org/euclid.aoms/1177692644; + + var v1, v2, v3, v4; + var s1, s2; + + do { + v1 = RANDOM() * 2 - 1; + v2 = RANDOM() * 2 - 1; + s1 = v1 * v1 + v2 * v2; + } while (s1 >= 1); + + do { + v3 = RANDOM() * 2 - 1; + v4 = RANDOM() * 2 - 1; + s2 = v3 * v3 + v4 * v4; + } while (s2 >= 1); + + var d = Math.sqrt((1 - s1) / s2); + out[0] = scale * v1; + out[1] = scale * v2; + out[2] = scale * v3 * d; + out[3] = scale * v4 * d; + return out; + } + /** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the vector to transform + * @param {ReadonlyMat4} m matrix to transform with + * @returns {vec4} out + */ + + function transformMat4$1(out, a, m) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; + } + /** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {ReadonlyVec4} a the vector to transform + * @param {ReadonlyQuat} q quaternion to transform with + * @returns {vec4} out + */ + + function transformQuat(out, a, q) { + var x = a[0], + y = a[1], + z = a[2]; + var qx = q[0], + qy = q[1], + qz = q[2], + qw = q[3]; // calculate quat * vec + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat + + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + out[3] = a[3]; + return out; + } + /** + * Set the components of a vec4 to zero + * + * @param {vec4} out the receiving vector + * @returns {vec4} out + */ + + function zero$1(out) { + out[0] = 0.0; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + return out; + } + /** + * Returns a string representation of a vector + * + * @param {ReadonlyVec4} a vector to represent as a string + * @returns {String} string representation of the vector + */ + + function str$3(a) { + return "vec4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")"; + } + /** + * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyVec4} a The first vector. + * @param {ReadonlyVec4} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function exactEquals$3(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; + } + /** + * Returns whether or not the vectors have approximately the same elements in the same position. + * + * @param {ReadonlyVec4} a The first vector. + * @param {ReadonlyVec4} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function equals$3(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)); + } + /** + * Alias for {@link vec4.subtract} + * @function + */ + + var sub$1 = subtract$1; + /** + * Alias for {@link vec4.multiply} + * @function + */ + + var mul$3 = multiply$3; + /** + * Alias for {@link vec4.divide} + * @function + */ + + var div$1 = divide$1; + /** + * Alias for {@link vec4.distance} + * @function + */ + + var dist$1 = distance$1; + /** + * Alias for {@link vec4.squaredDistance} + * @function + */ + + var sqrDist$1 = squaredDistance$1; + /** + * Alias for {@link vec4.length} + * @function + */ + + var len$3 = length$3; + /** + * Alias for {@link vec4.squaredLength} + * @function + */ + + var sqrLen$3 = squaredLength$3; + /** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + + var forEach$1 = function () { + var vec = create$3(); + return function (a, stride, offset, count, fn, arg) { + var i, l; + + if (!stride) { + stride = 4; + } + + if (!offset) { + offset = 0; + } + + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + + for (i = offset; i < l; i += stride) { + vec[0] = a[i]; + vec[1] = a[i + 1]; + vec[2] = a[i + 2]; + vec[3] = a[i + 3]; + fn(vec, vec, arg); + a[i] = vec[0]; + a[i + 1] = vec[1]; + a[i + 2] = vec[2]; + a[i + 3] = vec[3]; + } + + return a; + }; + }(); + + var vec4 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$3, + clone: clone$3, + fromValues: fromValues$3, + copy: copy$3, + set: set$3, + add: add$3, + subtract: subtract$1, + multiply: multiply$3, + divide: divide$1, + ceil: ceil$1, + floor: floor$1, + min: min$1, + max: max$1, + round: round$1, + scale: scale$3, + scaleAndAdd: scaleAndAdd$1, + distance: distance$1, + squaredDistance: squaredDistance$1, + length: length$3, + squaredLength: squaredLength$3, + negate: negate$1, + inverse: inverse$1, + normalize: normalize$3, + dot: dot$3, + cross: cross$1, + lerp: lerp$3, + random: random$2, + transformMat4: transformMat4$1, + transformQuat: transformQuat, + zero: zero$1, + str: str$3, + exactEquals: exactEquals$3, + equals: equals$3, + sub: sub$1, + mul: mul$3, + div: div$1, + dist: dist$1, + sqrDist: sqrDist$1, + len: len$3, + sqrLen: sqrLen$3, + forEach: forEach$1 + }); + + /** + * Quaternion in the format XYZW + * @module quat + */ + + /** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ + + function create$2() { + var out = new ARRAY_TYPE(4); + + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + + out[3] = 1; + return out; + } + /** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ + + function identity$1(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } + /** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyVec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ + + function setAxisAngle(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; + } + /** + * Gets the rotation axis and angle for a given + * quaternion. If a quaternion is created with + * setAxisAngle, this method will return the same + * values as providied in the original parameter list + * OR functionally equivalent values. + * Example: The quaternion formed by axis [0, 0, 1] and + * angle -90 is the same as the quaternion formed by + * [0, 0, 1] and 270. This method favors the latter. + * @param {vec3} out_axis Vector receiving the axis of rotation + * @param {ReadonlyQuat} q Quaternion to be decomposed + * @return {Number} Angle, in radians, of the rotation + */ + + function getAxisAngle(out_axis, q) { + var rad = Math.acos(q[3]) * 2.0; + var s = Math.sin(rad / 2.0); + + if (s > EPSILON) { + out_axis[0] = q[0] / s; + out_axis[1] = q[1] / s; + out_axis[2] = q[2] / s; + } else { + // If s is zero, return any axis (no rotation - axis does not matter) + out_axis[0] = 1; + out_axis[1] = 0; + out_axis[2] = 0; + } + + return rad; + } + /** + * Gets the angular distance between two unit quaternions + * + * @param {ReadonlyQuat} a Origin unit quaternion + * @param {ReadonlyQuat} b Destination unit quaternion + * @return {Number} Angle, in radians, between the two quaternions + */ + + function getAngle(a, b) { + var dotproduct = dot$2(a, b); + return Math.acos(2 * dotproduct * dotproduct - 1); + } + /** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @returns {quat} out + */ + + function multiply$2(out, a, b) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = b[0], + by = b[1], + bz = b[2], + bw = b[3]; + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; + } + /** + * Rotates a quaternion by the given angle about the X axis + * + * @param {quat} out quat receiving operation result + * @param {ReadonlyQuat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + + function rotateX$1(out, a, rad) { + rad *= 0.5; + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = Math.sin(rad), + bw = Math.cos(rad); + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; + } + /** + * Rotates a quaternion by the given angle about the Y axis + * + * @param {quat} out quat receiving operation result + * @param {ReadonlyQuat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + + function rotateY$1(out, a, rad) { + rad *= 0.5; + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var by = Math.sin(rad), + bw = Math.cos(rad); + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; + } + /** + * Rotates a quaternion by the given angle about the Z axis + * + * @param {quat} out quat receiving operation result + * @param {ReadonlyQuat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + + function rotateZ$1(out, a, rad) { + rad *= 0.5; + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bz = Math.sin(rad), + bw = Math.cos(rad); + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; + } + /** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate W component of + * @returns {quat} out + */ + + function calculateW(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; + } + /** + * Calculate the exponential of a unit quaternion. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate the exponential of + * @returns {quat} out + */ + + function exp(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var r = Math.sqrt(x * x + y * y + z * z); + var et = Math.exp(w); + var s = r > 0 ? et * Math.sin(r) / r : 0; + out[0] = x * s; + out[1] = y * s; + out[2] = z * s; + out[3] = et * Math.cos(r); + return out; + } + /** + * Calculate the natural logarithm of a unit quaternion. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate the exponential of + * @returns {quat} out + */ + + function ln(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var r = Math.sqrt(x * x + y * y + z * z); + var t = r > 0 ? Math.atan2(r, w) / r : 0; + out[0] = x * t; + out[1] = y * t; + out[2] = z * t; + out[3] = 0.5 * Math.log(x * x + y * y + z * z + w * w); + return out; + } + /** + * Calculate the scalar power of a unit quaternion. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate the exponential of + * @param {Number} b amount to scale the quaternion by + * @returns {quat} out + */ + + function pow(out, a, b) { + ln(out, a); + scale$2(out, out, b); + exp(out, out); + return out; + } + /** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {quat} out + */ + + function slerp(out, a, b, t) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + var bx = b[0], + by = b[1], + bz = b[2], + bw = b[3]; + var omega, cosom, sinom, scale0, scale1; // calc cosine + + cosom = ax * bx + ay * by + az * bz + aw * bw; // adjust signs (if necessary) + + if (cosom < 0.0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } // calculate coefficients + + + if (1.0 - cosom > EPSILON) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } // calculate final values + + + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + return out; + } + /** + * Generates a random unit quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ + + function random$1(out) { + // Implementation of http://planning.cs.uiuc.edu/node198.html + // TODO: Calling random 3 times is probably not the fastest solution + var u1 = RANDOM(); + var u2 = RANDOM(); + var u3 = RANDOM(); + var sqrt1MinusU1 = Math.sqrt(1 - u1); + var sqrtU1 = Math.sqrt(u1); + out[0] = sqrt1MinusU1 * Math.sin(2.0 * Math.PI * u2); + out[1] = sqrt1MinusU1 * Math.cos(2.0 * Math.PI * u2); + out[2] = sqrtU1 * Math.sin(2.0 * Math.PI * u3); + out[3] = sqrtU1 * Math.cos(2.0 * Math.PI * u3); + return out; + } + /** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate inverse of + * @returns {quat} out + */ + + function invert$1(out, a) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3]; + var dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; + var invDot = dot ? 1.0 / dot : 0; // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0 * invDot; + out[1] = -a1 * invDot; + out[2] = -a2 * invDot; + out[3] = a3 * invDot; + return out; + } + /** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quat to calculate conjugate of + * @returns {quat} out + */ + + function conjugate$1(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; + } + /** + * Creates a quaternion from the given 3x3 rotation matrix. + * + * NOTE: The resultant quaternion is not normalized, so you should be sure + * to renormalize the quaternion yourself where necessary. + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyMat3} m rotation matrix + * @returns {quat} out + * @function + */ + + function fromMat3(out, m) { + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + + if (fTrace > 0.0) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + + out[3] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; // 1/(4w) + + out[0] = (m[5] - m[7]) * fRoot; + out[1] = (m[6] - m[2]) * fRoot; + out[2] = (m[1] - m[3]) * fRoot; + } else { + // |w| <= 1/2 + var i = 0; + if (m[4] > m[0]) i = 1; + if (m[8] > m[i * 3 + i]) i = 2; + var j = (i + 1) % 3; + var k = (i + 2) % 3; + fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot; + out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot; + out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot; + } + + return out; + } + /** + * Creates a quaternion from the given euler angle x, y, z using the provided intrinsic order for the conversion. + * + * @param {quat} out the receiving quaternion + * @param {x} x Angle to rotate around X axis in degrees. + * @param {y} y Angle to rotate around Y axis in degrees. + * @param {z} z Angle to rotate around Z axis in degrees. + * @param {'zyx'|'xyz'|'yxz'|'yzx'|'zxy'|'zyx'} order Intrinsic order for conversion, default is zyx. + * @returns {quat} out + * @function + */ + + function fromEuler(out, x, y, z) { + var order = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : ANGLE_ORDER; + var halfToRad = Math.PI / 360; + x *= halfToRad; + z *= halfToRad; + y *= halfToRad; + var sx = Math.sin(x); + var cx = Math.cos(x); + var sy = Math.sin(y); + var cy = Math.cos(y); + var sz = Math.sin(z); + var cz = Math.cos(z); + + switch (order) { + case "xyz": + out[0] = sx * cy * cz + cx * sy * sz; + out[1] = cx * sy * cz - sx * cy * sz; + out[2] = cx * cy * sz + sx * sy * cz; + out[3] = cx * cy * cz - sx * sy * sz; + break; + + case "xzy": + out[0] = sx * cy * cz - cx * sy * sz; + out[1] = cx * sy * cz - sx * cy * sz; + out[2] = cx * cy * sz + sx * sy * cz; + out[3] = cx * cy * cz + sx * sy * sz; + break; + + case "yxz": + out[0] = sx * cy * cz + cx * sy * sz; + out[1] = cx * sy * cz - sx * cy * sz; + out[2] = cx * cy * sz - sx * sy * cz; + out[3] = cx * cy * cz + sx * sy * sz; + break; + + case "yzx": + out[0] = sx * cy * cz + cx * sy * sz; + out[1] = cx * sy * cz + sx * cy * sz; + out[2] = cx * cy * sz - sx * sy * cz; + out[3] = cx * cy * cz - sx * sy * sz; + break; + + case "zxy": + out[0] = sx * cy * cz - cx * sy * sz; + out[1] = cx * sy * cz + sx * cy * sz; + out[2] = cx * cy * sz + sx * sy * cz; + out[3] = cx * cy * cz - sx * sy * sz; + break; + + case "zyx": + out[0] = sx * cy * cz - cx * sy * sz; + out[1] = cx * sy * cz + sx * cy * sz; + out[2] = cx * cy * sz - sx * sy * cz; + out[3] = cx * cy * cz + sx * sy * sz; + break; + + default: + throw new Error('Unknown angle order ' + order); + } + + return out; + } + /** + * Returns a string representation of a quaternion + * + * @param {ReadonlyQuat} a vector to represent as a string + * @returns {String} string representation of the vector + */ + + function str$2(a) { + return "quat(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")"; + } + /** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {ReadonlyQuat} a quaternion to clone + * @returns {quat} a new quaternion + * @function + */ + + var clone$2 = clone$3; + /** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + * @function + */ + + var fromValues$2 = fromValues$3; + /** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the source quaternion + * @returns {quat} out + * @function + */ + + var copy$2 = copy$3; + /** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + * @function + */ + + var set$2 = set$3; + /** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @returns {quat} out + * @function + */ + + var add$2 = add$3; + /** + * Alias for {@link quat.multiply} + * @function + */ + + var mul$2 = multiply$2; + /** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {ReadonlyQuat} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {quat} out + * @function + */ + + var scale$2 = scale$3; + /** + * Calculates the dot product of two quat's + * + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @returns {Number} dot product of a and b + * @function + */ + + var dot$2 = dot$3; + /** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {quat} out + * @function + */ + + var lerp$2 = lerp$3; + /** + * Calculates the length of a quat + * + * @param {ReadonlyQuat} a vector to calculate length of + * @returns {Number} length of a + */ + + var length$2 = length$3; + /** + * Alias for {@link quat.length} + * @function + */ + + var len$2 = length$2; + /** + * Calculates the squared length of a quat + * + * @param {ReadonlyQuat} a vector to calculate squared length of + * @returns {Number} squared length of a + * @function + */ + + var squaredLength$2 = squaredLength$3; + /** + * Alias for {@link quat.squaredLength} + * @function + */ + + var sqrLen$2 = squaredLength$2; + /** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a quaternion to normalize + * @returns {quat} out + * @function + */ + + var normalize$2 = normalize$3; + /** + * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyQuat} a The first quaternion. + * @param {ReadonlyQuat} b The second quaternion. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + var exactEquals$2 = exactEquals$3; + /** + * Returns whether or not the quaternions point approximately to the same direction. + * + * Both quaternions are assumed to be unit length. + * + * @param {ReadonlyQuat} a The first unit quaternion. + * @param {ReadonlyQuat} b The second unit quaternion. + * @returns {Boolean} True if the quaternions are equal, false otherwise. + */ + + function equals$2(a, b) { + return Math.abs(dot$3(a, b)) >= 1 - EPSILON; + } + /** + * Sets a quaternion to represent the shortest rotation from one + * vector to another. + * + * Both vectors are assumed to be unit length. + * + * @param {quat} out the receiving quaternion. + * @param {ReadonlyVec3} a the initial vector + * @param {ReadonlyVec3} b the destination vector + * @returns {quat} out + */ + + var rotationTo = function () { + var tmpvec3 = create$4(); + var xUnitVec3 = fromValues$4(1, 0, 0); + var yUnitVec3 = fromValues$4(0, 1, 0); + return function (out, a, b) { + var dot = dot$4(a, b); + + if (dot < -0.999999) { + cross$2(tmpvec3, xUnitVec3, a); + if (len$4(tmpvec3) < 0.000001) cross$2(tmpvec3, yUnitVec3, a); + normalize$4(tmpvec3, tmpvec3); + setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross$2(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return normalize$2(out, out); + } + }; + }(); + /** + * Performs a spherical linear interpolation with two control points + * + * @param {quat} out the receiving quaternion + * @param {ReadonlyQuat} a the first operand + * @param {ReadonlyQuat} b the second operand + * @param {ReadonlyQuat} c the third operand + * @param {ReadonlyQuat} d the fourth operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {quat} out + */ + + var sqlerp = function () { + var temp1 = create$2(); + var temp2 = create$2(); + return function (out, a, b, c, d, t) { + slerp(temp1, a, d, t); + slerp(temp2, b, c, t); + slerp(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; + }(); + /** + * Sets the specified quaternion with values corresponding to the given + * axes. Each axis is a vec3 and is expected to be unit length and + * perpendicular to all other specified axes. + * + * @param {ReadonlyVec3} view the vector representing the viewing direction + * @param {ReadonlyVec3} right the vector representing the local "right" direction + * @param {ReadonlyVec3} up the vector representing the local "up" direction + * @returns {quat} out + */ + + var setAxes = function () { + var matr = create$6(); + return function (out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + return normalize$2(out, fromMat3(out, matr)); + }; + }(); + + var quat = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$2, + identity: identity$1, + setAxisAngle: setAxisAngle, + getAxisAngle: getAxisAngle, + getAngle: getAngle, + multiply: multiply$2, + rotateX: rotateX$1, + rotateY: rotateY$1, + rotateZ: rotateZ$1, + calculateW: calculateW, + exp: exp, + ln: ln, + pow: pow, + slerp: slerp, + random: random$1, + invert: invert$1, + conjugate: conjugate$1, + fromMat3: fromMat3, + fromEuler: fromEuler, + str: str$2, + clone: clone$2, + fromValues: fromValues$2, + copy: copy$2, + set: set$2, + add: add$2, + mul: mul$2, + scale: scale$2, + dot: dot$2, + lerp: lerp$2, + length: length$2, + len: len$2, + squaredLength: squaredLength$2, + sqrLen: sqrLen$2, + normalize: normalize$2, + exactEquals: exactEquals$2, + equals: equals$2, + rotationTo: rotationTo, + sqlerp: sqlerp, + setAxes: setAxes + }); + + /** + * Dual Quaternion
    + * Format: [real, dual]
    + * Quaternion format: XYZW
    + * Make sure to have normalized dual quaternions, otherwise the functions may not work as intended.
    + * @module quat2 + */ + + /** + * Creates a new identity dual quat + * + * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation] + */ + + function create$1() { + var dq = new ARRAY_TYPE(8); + + if (ARRAY_TYPE != Float32Array) { + dq[0] = 0; + dq[1] = 0; + dq[2] = 0; + dq[4] = 0; + dq[5] = 0; + dq[6] = 0; + dq[7] = 0; + } + + dq[3] = 1; + return dq; + } + /** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {ReadonlyQuat2} a dual quaternion to clone + * @returns {quat2} new dual quaternion + * @function + */ + + function clone$1(a) { + var dq = new ARRAY_TYPE(8); + dq[0] = a[0]; + dq[1] = a[1]; + dq[2] = a[2]; + dq[3] = a[3]; + dq[4] = a[4]; + dq[5] = a[5]; + dq[6] = a[6]; + dq[7] = a[7]; + return dq; + } + /** + * Creates a new dual quat initialized with the given values + * + * @param {Number} x1 X component + * @param {Number} y1 Y component + * @param {Number} z1 Z component + * @param {Number} w1 W component + * @param {Number} x2 X component + * @param {Number} y2 Y component + * @param {Number} z2 Z component + * @param {Number} w2 W component + * @returns {quat2} new dual quaternion + * @function + */ + + function fromValues$1(x1, y1, z1, w1, x2, y2, z2, w2) { + var dq = new ARRAY_TYPE(8); + dq[0] = x1; + dq[1] = y1; + dq[2] = z1; + dq[3] = w1; + dq[4] = x2; + dq[5] = y2; + dq[6] = z2; + dq[7] = w2; + return dq; + } + /** + * Creates a new dual quat from the given values (quat and translation) + * + * @param {Number} x1 X component + * @param {Number} y1 Y component + * @param {Number} z1 Z component + * @param {Number} w1 W component + * @param {Number} x2 X component (translation) + * @param {Number} y2 Y component (translation) + * @param {Number} z2 Z component (translation) + * @returns {quat2} new dual quaternion + * @function + */ + + function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) { + var dq = new ARRAY_TYPE(8); + dq[0] = x1; + dq[1] = y1; + dq[2] = z1; + dq[3] = w1; + var ax = x2 * 0.5, + ay = y2 * 0.5, + az = z2 * 0.5; + dq[4] = ax * w1 + ay * z1 - az * y1; + dq[5] = ay * w1 + az * x1 - ax * z1; + dq[6] = az * w1 + ax * y1 - ay * x1; + dq[7] = -ax * x1 - ay * y1 - az * z1; + return dq; + } + /** + * Creates a dual quat from a quaternion and a translation + * + * @param {ReadonlyQuat2} dual quaternion receiving operation result + * @param {ReadonlyQuat} q a normalized quaternion + * @param {ReadonlyVec3} t translation vector + * @returns {quat2} dual quaternion receiving operation result + * @function + */ + + function fromRotationTranslation(out, q, t) { + var ax = t[0] * 0.5, + ay = t[1] * 0.5, + az = t[2] * 0.5, + bx = q[0], + by = q[1], + bz = q[2], + bw = q[3]; + out[0] = bx; + out[1] = by; + out[2] = bz; + out[3] = bw; + out[4] = ax * bw + ay * bz - az * by; + out[5] = ay * bw + az * bx - ax * bz; + out[6] = az * bw + ax * by - ay * bx; + out[7] = -ax * bx - ay * by - az * bz; + return out; + } + /** + * Creates a dual quat from a translation + * + * @param {ReadonlyQuat2} dual quaternion receiving operation result + * @param {ReadonlyVec3} t translation vector + * @returns {quat2} dual quaternion receiving operation result + * @function + */ + + function fromTranslation(out, t) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = t[0] * 0.5; + out[5] = t[1] * 0.5; + out[6] = t[2] * 0.5; + out[7] = 0; + return out; + } + /** + * Creates a dual quat from a quaternion + * + * @param {ReadonlyQuat2} dual quaternion receiving operation result + * @param {ReadonlyQuat} q the quaternion + * @returns {quat2} dual quaternion receiving operation result + * @function + */ + + function fromRotation(out, q) { + out[0] = q[0]; + out[1] = q[1]; + out[2] = q[2]; + out[3] = q[3]; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + return out; + } + /** + * Creates a new dual quat from a matrix (4x4) + * + * @param {quat2} out the dual quaternion + * @param {ReadonlyMat4} a the matrix + * @returns {quat2} dual quat receiving operation result + * @function + */ + + function fromMat4(out, a) { + //TODO Optimize this + var outer = create$2(); + getRotation(outer, a); + var t = new ARRAY_TYPE(3); + getTranslation$1(t, a); + fromRotationTranslation(out, outer, t); + return out; + } + /** + * Copy the values from one dual quat to another + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the source dual quaternion + * @returns {quat2} out + * @function + */ + + function copy$1(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + return out; + } + /** + * Set a dual quat to the identity dual quaternion + * + * @param {quat2} out the receiving quaternion + * @returns {quat2} out + */ + + function identity(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + return out; + } + /** + * Set the components of a dual quat to the given values + * + * @param {quat2} out the receiving quaternion + * @param {Number} x1 X component + * @param {Number} y1 Y component + * @param {Number} z1 Z component + * @param {Number} w1 W component + * @param {Number} x2 X component + * @param {Number} y2 Y component + * @param {Number} z2 Z component + * @param {Number} w2 W component + * @returns {quat2} out + * @function + */ + + function set$1(out, x1, y1, z1, w1, x2, y2, z2, w2) { + out[0] = x1; + out[1] = y1; + out[2] = z1; + out[3] = w1; + out[4] = x2; + out[5] = y2; + out[6] = z2; + out[7] = w2; + return out; + } + /** + * Gets the real part of a dual quat + * @param {quat} out real part + * @param {ReadonlyQuat2} a Dual Quaternion + * @return {quat} real part + */ + + var getReal = copy$2; + /** + * Gets the dual part of a dual quat + * @param {quat} out dual part + * @param {ReadonlyQuat2} a Dual Quaternion + * @return {quat} dual part + */ + + function getDual(out, a) { + out[0] = a[4]; + out[1] = a[5]; + out[2] = a[6]; + out[3] = a[7]; + return out; + } + /** + * Set the real component of a dual quat to the given quaternion + * + * @param {quat2} out the receiving quaternion + * @param {ReadonlyQuat} q a quaternion representing the real part + * @returns {quat2} out + * @function + */ + + var setReal = copy$2; + /** + * Set the dual component of a dual quat to the given quaternion + * + * @param {quat2} out the receiving quaternion + * @param {ReadonlyQuat} q a quaternion representing the dual part + * @returns {quat2} out + * @function + */ + + function setDual(out, q) { + out[4] = q[0]; + out[5] = q[1]; + out[6] = q[2]; + out[7] = q[3]; + return out; + } + /** + * Gets the translation of a normalized dual quat + * @param {vec3} out translation + * @param {ReadonlyQuat2} a Dual Quaternion to be decomposed + * @return {vec3} translation + */ + + function getTranslation(out, a) { + var ax = a[4], + ay = a[5], + az = a[6], + aw = a[7], + bx = -a[0], + by = -a[1], + bz = -a[2], + bw = a[3]; + out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2; + out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2; + out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2; + return out; + } + /** + * Translates a dual quat by the given vector + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to translate + * @param {ReadonlyVec3} v vector to translate by + * @returns {quat2} out + */ + + function translate(out, a, v) { + var ax1 = a[0], + ay1 = a[1], + az1 = a[2], + aw1 = a[3], + bx1 = v[0] * 0.5, + by1 = v[1] * 0.5, + bz1 = v[2] * 0.5, + ax2 = a[4], + ay2 = a[5], + az2 = a[6], + aw2 = a[7]; + out[0] = ax1; + out[1] = ay1; + out[2] = az1; + out[3] = aw1; + out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2; + out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2; + out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2; + out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2; + return out; + } + /** + * Rotates a dual quat around the X axis + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @param {number} rad how far should the rotation be + * @returns {quat2} out + */ + + function rotateX(out, a, rad) { + var bx = -a[0], + by = -a[1], + bz = -a[2], + bw = a[3], + ax = a[4], + ay = a[5], + az = a[6], + aw = a[7], + ax1 = ax * bw + aw * bx + ay * bz - az * by, + ay1 = ay * bw + aw * by + az * bx - ax * bz, + az1 = az * bw + aw * bz + ax * by - ay * bx, + aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateX$1(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; + } + /** + * Rotates a dual quat around the Y axis + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @param {number} rad how far should the rotation be + * @returns {quat2} out + */ + + function rotateY(out, a, rad) { + var bx = -a[0], + by = -a[1], + bz = -a[2], + bw = a[3], + ax = a[4], + ay = a[5], + az = a[6], + aw = a[7], + ax1 = ax * bw + aw * bx + ay * bz - az * by, + ay1 = ay * bw + aw * by + az * bx - ax * bz, + az1 = az * bw + aw * bz + ax * by - ay * bx, + aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateY$1(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; + } + /** + * Rotates a dual quat around the Z axis + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @param {number} rad how far should the rotation be + * @returns {quat2} out + */ + + function rotateZ(out, a, rad) { + var bx = -a[0], + by = -a[1], + bz = -a[2], + bw = a[3], + ax = a[4], + ay = a[5], + az = a[6], + aw = a[7], + ax1 = ax * bw + aw * bx + ay * bz - az * by, + ay1 = ay * bw + aw * by + az * bx - ax * bz, + az1 = az * bw + aw * bz + ax * by - ay * bx, + aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateZ$1(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; + } + /** + * Rotates a dual quat by a given quaternion (a * q) + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @param {ReadonlyQuat} q quaternion to rotate by + * @returns {quat2} out + */ + + function rotateByQuatAppend(out, a, q) { + var qx = q[0], + qy = q[1], + qz = q[2], + qw = q[3], + ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax * qw + aw * qx + ay * qz - az * qy; + out[1] = ay * qw + aw * qy + az * qx - ax * qz; + out[2] = az * qw + aw * qz + ax * qy - ay * qx; + out[3] = aw * qw - ax * qx - ay * qy - az * qz; + ax = a[4]; + ay = a[5]; + az = a[6]; + aw = a[7]; + out[4] = ax * qw + aw * qx + ay * qz - az * qy; + out[5] = ay * qw + aw * qy + az * qx - ax * qz; + out[6] = az * qw + aw * qz + ax * qy - ay * qx; + out[7] = aw * qw - ax * qx - ay * qy - az * qz; + return out; + } + /** + * Rotates a dual quat by a given quaternion (q * a) + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat} q quaternion to rotate by + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @returns {quat2} out + */ + + function rotateByQuatPrepend(out, q, a) { + var qx = q[0], + qy = q[1], + qz = q[2], + qw = q[3], + bx = a[0], + by = a[1], + bz = a[2], + bw = a[3]; + out[0] = qx * bw + qw * bx + qy * bz - qz * by; + out[1] = qy * bw + qw * by + qz * bx - qx * bz; + out[2] = qz * bw + qw * bz + qx * by - qy * bx; + out[3] = qw * bw - qx * bx - qy * by - qz * bz; + bx = a[4]; + by = a[5]; + bz = a[6]; + bw = a[7]; + out[4] = qx * bw + qw * bx + qy * bz - qz * by; + out[5] = qy * bw + qw * by + qz * bx - qx * bz; + out[6] = qz * bw + qw * bz + qx * by - qy * bx; + out[7] = qw * bw - qx * bx - qy * by - qz * bz; + return out; + } + /** + * Rotates a dual quat around a given axis. Does the normalisation automatically + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the dual quaternion to rotate + * @param {ReadonlyVec3} axis the axis to rotate around + * @param {Number} rad how far the rotation should be + * @returns {quat2} out + */ + + function rotateAroundAxis(out, a, axis, rad) { + //Special case for rad = 0 + if (Math.abs(rad) < EPSILON) { + return copy$1(out, a); + } + + var axisLength = Math.hypot(axis[0], axis[1], axis[2]); + rad = rad * 0.5; + var s = Math.sin(rad); + var bx = s * axis[0] / axisLength; + var by = s * axis[1] / axisLength; + var bz = s * axis[2] / axisLength; + var bw = Math.cos(rad); + var ax1 = a[0], + ay1 = a[1], + az1 = a[2], + aw1 = a[3]; + out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + var ax = a[4], + ay = a[5], + az = a[6], + aw = a[7]; + out[4] = ax * bw + aw * bx + ay * bz - az * by; + out[5] = ay * bw + aw * by + az * bx - ax * bz; + out[6] = az * bw + aw * bz + ax * by - ay * bx; + out[7] = aw * bw - ax * bx - ay * by - az * bz; + return out; + } + /** + * Adds two dual quat's + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the first operand + * @param {ReadonlyQuat2} b the second operand + * @returns {quat2} out + * @function + */ + + function add$1(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + return out; + } + /** + * Multiplies two dual quat's + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a the first operand + * @param {ReadonlyQuat2} b the second operand + * @returns {quat2} out + */ + + function multiply$1(out, a, b) { + var ax0 = a[0], + ay0 = a[1], + az0 = a[2], + aw0 = a[3], + bx1 = b[4], + by1 = b[5], + bz1 = b[6], + bw1 = b[7], + ax1 = a[4], + ay1 = a[5], + az1 = a[6], + aw1 = a[7], + bx0 = b[0], + by0 = b[1], + bz0 = b[2], + bw0 = b[3]; + out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0; + out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0; + out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0; + out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0; + out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0; + out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0; + out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0; + out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0; + return out; + } + /** + * Alias for {@link quat2.multiply} + * @function + */ + + var mul$1 = multiply$1; + /** + * Scales a dual quat by a scalar number + * + * @param {quat2} out the receiving dual quat + * @param {ReadonlyQuat2} a the dual quat to scale + * @param {Number} b amount to scale the dual quat by + * @returns {quat2} out + * @function + */ + + function scale$1(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + out[6] = a[6] * b; + out[7] = a[7] * b; + return out; + } + /** + * Calculates the dot product of two dual quat's (The dot product of the real parts) + * + * @param {ReadonlyQuat2} a the first operand + * @param {ReadonlyQuat2} b the second operand + * @returns {Number} dot product of a and b + * @function + */ + + var dot$1 = dot$2; + /** + * Performs a linear interpolation between two dual quats's + * NOTE: The resulting dual quaternions won't always be normalized (The error is most noticeable when t = 0.5) + * + * @param {quat2} out the receiving dual quat + * @param {ReadonlyQuat2} a the first operand + * @param {ReadonlyQuat2} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {quat2} out + */ + + function lerp$1(out, a, b, t) { + var mt = 1 - t; + if (dot$1(a, b) < 0) t = -t; + out[0] = a[0] * mt + b[0] * t; + out[1] = a[1] * mt + b[1] * t; + out[2] = a[2] * mt + b[2] * t; + out[3] = a[3] * mt + b[3] * t; + out[4] = a[4] * mt + b[4] * t; + out[5] = a[5] * mt + b[5] * t; + out[6] = a[6] * mt + b[6] * t; + out[7] = a[7] * mt + b[7] * t; + return out; + } + /** + * Calculates the inverse of a dual quat. If they are normalized, conjugate is cheaper + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a dual quat to calculate inverse of + * @returns {quat2} out + */ + + function invert(out, a) { + var sqlen = squaredLength$1(a); + out[0] = -a[0] / sqlen; + out[1] = -a[1] / sqlen; + out[2] = -a[2] / sqlen; + out[3] = a[3] / sqlen; + out[4] = -a[4] / sqlen; + out[5] = -a[5] / sqlen; + out[6] = -a[6] / sqlen; + out[7] = a[7] / sqlen; + return out; + } + /** + * Calculates the conjugate of a dual quat + * If the dual quaternion is normalized, this function is faster than quat2.inverse and produces the same result. + * + * @param {quat2} out the receiving quaternion + * @param {ReadonlyQuat2} a quat to calculate conjugate of + * @returns {quat2} out + */ + + function conjugate(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + out[4] = -a[4]; + out[5] = -a[5]; + out[6] = -a[6]; + out[7] = a[7]; + return out; + } + /** + * Calculates the length of a dual quat + * + * @param {ReadonlyQuat2} a dual quat to calculate length of + * @returns {Number} length of a + * @function + */ + + var length$1 = length$2; + /** + * Alias for {@link quat2.length} + * @function + */ + + var len$1 = length$1; + /** + * Calculates the squared length of a dual quat + * + * @param {ReadonlyQuat2} a dual quat to calculate squared length of + * @returns {Number} squared length of a + * @function + */ + + var squaredLength$1 = squaredLength$2; + /** + * Alias for {@link quat2.squaredLength} + * @function + */ + + var sqrLen$1 = squaredLength$1; + /** + * Normalize a dual quat + * + * @param {quat2} out the receiving dual quaternion + * @param {ReadonlyQuat2} a dual quaternion to normalize + * @returns {quat2} out + * @function + */ + + function normalize$1(out, a) { + var magnitude = squaredLength$1(a); + + if (magnitude > 0) { + magnitude = Math.sqrt(magnitude); + var a0 = a[0] / magnitude; + var a1 = a[1] / magnitude; + var a2 = a[2] / magnitude; + var a3 = a[3] / magnitude; + var b0 = a[4]; + var b1 = a[5]; + var b2 = a[6]; + var b3 = a[7]; + var a_dot_b = a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3; + out[0] = a0; + out[1] = a1; + out[2] = a2; + out[3] = a3; + out[4] = (b0 - a0 * a_dot_b) / magnitude; + out[5] = (b1 - a1 * a_dot_b) / magnitude; + out[6] = (b2 - a2 * a_dot_b) / magnitude; + out[7] = (b3 - a3 * a_dot_b) / magnitude; + } + + return out; + } + /** + * Returns a string representation of a dual quaternion + * + * @param {ReadonlyQuat2} a dual quaternion to represent as a string + * @returns {String} string representation of the dual quat + */ + + function str$1(a) { + return "quat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ")"; + } + /** + * Returns whether or not the dual quaternions have exactly the same elements in the same position (when compared with ===) + * + * @param {ReadonlyQuat2} a the first dual quaternion. + * @param {ReadonlyQuat2} b the second dual quaternion. + * @returns {Boolean} true if the dual quaternions are equal, false otherwise. + */ + + function exactEquals$1(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7]; + } + /** + * Returns whether or not the dual quaternions have approximately the same elements in the same position. + * + * @param {ReadonlyQuat2} a the first dual quat. + * @param {ReadonlyQuat2} b the second dual quat. + * @returns {Boolean} true if the dual quats are equal, false otherwise. + */ + + function equals$1(a, b) { + var a0 = a[0], + a1 = a[1], + a2 = a[2], + a3 = a[3], + a4 = a[4], + a5 = a[5], + a6 = a[6], + a7 = a[7]; + var b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)); + } + + var quat2 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$1, + clone: clone$1, + fromValues: fromValues$1, + fromRotationTranslationValues: fromRotationTranslationValues, + fromRotationTranslation: fromRotationTranslation, + fromTranslation: fromTranslation, + fromRotation: fromRotation, + fromMat4: fromMat4, + copy: copy$1, + identity: identity, + set: set$1, + getReal: getReal, + getDual: getDual, + setReal: setReal, + setDual: setDual, + getTranslation: getTranslation, + translate: translate, + rotateX: rotateX, + rotateY: rotateY, + rotateZ: rotateZ, + rotateByQuatAppend: rotateByQuatAppend, + rotateByQuatPrepend: rotateByQuatPrepend, + rotateAroundAxis: rotateAroundAxis, + add: add$1, + multiply: multiply$1, + mul: mul$1, + scale: scale$1, + dot: dot$1, + lerp: lerp$1, + invert: invert, + conjugate: conjugate, + length: length$1, + len: len$1, + squaredLength: squaredLength$1, + sqrLen: sqrLen$1, + normalize: normalize$1, + str: str$1, + exactEquals: exactEquals$1, + equals: equals$1 + }); + + /** + * 2 Dimensional Vector + * @module vec2 + */ + + /** + * Creates a new, empty vec2 + * + * @returns {vec2} a new 2D vector + */ + + function create() { + var out = new ARRAY_TYPE(2); + + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + } + + return out; + } + /** + * Creates a new vec2 initialized with values from an existing vector + * + * @param {ReadonlyVec2} a vector to clone + * @returns {vec2} a new 2D vector + */ + + function clone(a) { + var out = new ARRAY_TYPE(2); + out[0] = a[0]; + out[1] = a[1]; + return out; + } + /** + * Creates a new vec2 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} a new 2D vector + */ + + function fromValues(x, y) { + var out = new ARRAY_TYPE(2); + out[0] = x; + out[1] = y; + return out; + } + /** + * Copy the values from one vec2 to another + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the source vector + * @returns {vec2} out + */ + + function copy(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; + } + /** + * Set the components of a vec2 to the given values + * + * @param {vec2} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} out + */ + + function set(out, x, y) { + out[0] = x; + out[1] = y; + return out; + } + /** + * Adds two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function add(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; + } + /** + * Subtracts vector b from vector a + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function subtract(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; + } + /** + * Multiplies two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function multiply(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; + } + /** + * Divides two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function divide(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; + } + /** + * Math.ceil the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to ceil + * @returns {vec2} out + */ + + function ceil(out, a) { + out[0] = Math.ceil(a[0]); + out[1] = Math.ceil(a[1]); + return out; + } + /** + * Math.floor the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to floor + * @returns {vec2} out + */ + + function floor(out, a) { + out[0] = Math.floor(a[0]); + out[1] = Math.floor(a[1]); + return out; + } + /** + * Returns the minimum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function min(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + return out; + } + /** + * Returns the maximum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec2} out + */ + + function max(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + return out; + } + /** + * Math.round the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to round + * @returns {vec2} out + */ + + function round(out, a) { + out[0] = Math.round(a[0]); + out[1] = Math.round(a[1]); + return out; + } + /** + * Scales a vec2 by a scalar number + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec2} out + */ + + function scale(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; + } + /** + * Adds two vec2's after scaling the second operand by a scalar value + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec2} out + */ + + function scaleAndAdd(out, a, b, scale) { + out[0] = a[0] + b[0] * scale; + out[1] = a[1] + b[1] * scale; + return out; + } + /** + * Calculates the euclidian distance between two vec2's + * + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {Number} distance between a and b + */ + + function distance(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.hypot(x, y); + } + /** + * Calculates the squared euclidian distance between two vec2's + * + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {Number} squared distance between a and b + */ + + function squaredDistance(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x * x + y * y; + } + /** + * Calculates the length of a vec2 + * + * @param {ReadonlyVec2} a vector to calculate length of + * @returns {Number} length of a + */ + + function length(a) { + var x = a[0], + y = a[1]; + return Math.hypot(x, y); + } + /** + * Calculates the squared length of a vec2 + * + * @param {ReadonlyVec2} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + + function squaredLength(a) { + var x = a[0], + y = a[1]; + return x * x + y * y; + } + /** + * Negates the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to negate + * @returns {vec2} out + */ + + function negate(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; + } + /** + * Returns the inverse of the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to invert + * @returns {vec2} out + */ + + function inverse(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + return out; + } + /** + * Normalize a vec2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a vector to normalize + * @returns {vec2} out + */ + + function normalize(out, a) { + var x = a[0], + y = a[1]; + var len = x * x + y * y; + + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + } + + out[0] = a[0] * len; + out[1] = a[1] * len; + return out; + } + /** + * Calculates the dot product of two vec2's + * + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {Number} dot product of a and b + */ + + function dot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + /** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @returns {vec3} out + */ + + function cross(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; + } + /** + * Performs a linear interpolation between two vec2's + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the first operand + * @param {ReadonlyVec2} b the second operand + * @param {Number} t interpolation amount, in the range [0-1], between the two inputs + * @returns {vec2} out + */ + + function lerp(out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; + } + /** + * Generates a random vector with the given scale + * + * @param {vec2} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned + * @returns {vec2} out + */ + + function random(out, scale) { + scale = scale || 1.0; + var r = RANDOM() * 2.0 * Math.PI; + out[0] = Math.cos(r) * scale; + out[1] = Math.sin(r) * scale; + return out; + } + /** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the vector to transform + * @param {ReadonlyMat2} m matrix to transform with + * @returns {vec2} out + */ + + function transformMat2(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y; + out[1] = m[1] * x + m[3] * y; + return out; + } + /** + * Transforms the vec2 with a mat2d + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the vector to transform + * @param {ReadonlyMat2d} m matrix to transform with + * @returns {vec2} out + */ + + function transformMat2d(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + } + /** + * Transforms the vec2 with a mat3 + * 3rd vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the vector to transform + * @param {ReadonlyMat3} m matrix to transform with + * @returns {vec2} out + */ + + function transformMat3(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[3] * y + m[6]; + out[1] = m[1] * x + m[4] * y + m[7]; + return out; + } + /** + * Transforms the vec2 with a mat4 + * 3rd vector component is implicitly '0' + * 4th vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {ReadonlyVec2} a the vector to transform + * @param {ReadonlyMat4} m matrix to transform with + * @returns {vec2} out + */ + + function transformMat4(out, a, m) { + var x = a[0]; + var y = a[1]; + out[0] = m[0] * x + m[4] * y + m[12]; + out[1] = m[1] * x + m[5] * y + m[13]; + return out; + } + /** + * Rotate a 2D vector + * @param {vec2} out The receiving vec2 + * @param {ReadonlyVec2} a The vec2 point to rotate + * @param {ReadonlyVec2} b The origin of the rotation + * @param {Number} rad The angle of rotation in radians + * @returns {vec2} out + */ + + function rotate(out, a, b, rad) { + //Translate point to the origin + var p0 = a[0] - b[0], + p1 = a[1] - b[1], + sinC = Math.sin(rad), + cosC = Math.cos(rad); //perform rotation and translate to correct position + + out[0] = p0 * cosC - p1 * sinC + b[0]; + out[1] = p0 * sinC + p1 * cosC + b[1]; + return out; + } + /** + * Get the angle between two 2D vectors + * @param {ReadonlyVec2} a The first operand + * @param {ReadonlyVec2} b The second operand + * @returns {Number} The angle in radians + */ + + function angle(a, b) { + var x1 = a[0], + y1 = a[1], + x2 = b[0], + y2 = b[1], + // mag is the product of the magnitudes of a and b + mag = Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)), + // mag &&.. short circuits if mag == 0 + cosine = mag && (x1 * x2 + y1 * y2) / mag; // Math.min(Math.max(cosine, -1), 1) clamps the cosine between -1 and 1 + + return Math.acos(Math.min(Math.max(cosine, -1), 1)); + } + /** + * Set the components of a vec2 to zero + * + * @param {vec2} out the receiving vector + * @returns {vec2} out + */ + + function zero(out) { + out[0] = 0.0; + out[1] = 0.0; + return out; + } + /** + * Returns a string representation of a vector + * + * @param {ReadonlyVec2} a vector to represent as a string + * @returns {String} string representation of the vector + */ + + function str(a) { + return "vec2(" + a[0] + ", " + a[1] + ")"; + } + /** + * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===) + * + * @param {ReadonlyVec2} a The first vector. + * @param {ReadonlyVec2} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function exactEquals(a, b) { + return a[0] === b[0] && a[1] === b[1]; + } + /** + * Returns whether or not the vectors have approximately the same elements in the same position. + * + * @param {ReadonlyVec2} a The first vector. + * @param {ReadonlyVec2} b The second vector. + * @returns {Boolean} True if the vectors are equal, false otherwise. + */ + + function equals(a, b) { + var a0 = a[0], + a1 = a[1]; + var b0 = b[0], + b1 = b[1]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)); + } + /** + * Alias for {@link vec2.length} + * @function + */ + + var len = length; + /** + * Alias for {@link vec2.subtract} + * @function + */ + + var sub = subtract; + /** + * Alias for {@link vec2.multiply} + * @function + */ + + var mul = multiply; + /** + * Alias for {@link vec2.divide} + * @function + */ + + var div = divide; + /** + * Alias for {@link vec2.distance} + * @function + */ + + var dist = distance; + /** + * Alias for {@link vec2.squaredDistance} + * @function + */ + + var sqrDist = squaredDistance; + /** + * Alias for {@link vec2.squaredLength} + * @function + */ + + var sqrLen = squaredLength; + /** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + + var forEach = function () { + var vec = create(); + return function (a, stride, offset, count, fn, arg) { + var i, l; + + if (!stride) { + stride = 2; + } + + if (!offset) { + offset = 0; + } + + if (count) { + l = Math.min(count * stride + offset, a.length); + } else { + l = a.length; + } + + for (i = offset; i < l; i += stride) { + vec[0] = a[i]; + vec[1] = a[i + 1]; + fn(vec, vec, arg); + a[i] = vec[0]; + a[i + 1] = vec[1]; + } + + return a; + }; + }(); + + var vec2 = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create, + clone: clone, + fromValues: fromValues, + copy: copy, + set: set, + add: add, + subtract: subtract, + multiply: multiply, + divide: divide, + ceil: ceil, + floor: floor, + min: min, + max: max, + round: round, + scale: scale, + scaleAndAdd: scaleAndAdd, + distance: distance, + squaredDistance: squaredDistance, + length: length, + squaredLength: squaredLength, + negate: negate, + inverse: inverse, + normalize: normalize, + dot: dot, + cross: cross, + lerp: lerp, + random: random, + transformMat2: transformMat2, + transformMat2d: transformMat2d, + transformMat3: transformMat3, + transformMat4: transformMat4, + rotate: rotate, + angle: angle, + zero: zero, + str: str, + exactEquals: exactEquals, + equals: equals, + len: len, + sub: sub, + mul: mul, + div: div, + dist: dist, + sqrDist: sqrDist, + sqrLen: sqrLen, + forEach: forEach + }); + + exports.glMatrix = common; + exports.mat2 = mat2; + exports.mat2d = mat2d; + exports.mat3 = mat3; + exports.mat4 = mat4; + exports.quat = quat; + exports.quat2 = quat2; + exports.vec2 = vec2; + exports.vec3 = vec3; + exports.vec4 = vec4; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/scripts/hardware_wallet.js b/scripts/hardware_wallet.js new file mode 100755 index 0000000..906e58d --- /dev/null +++ b/scripts/hardware_wallet.js @@ -0,0 +1,7062 @@ +// Use strict +"use strict"; + + +// Classes + +// HardwareWallet class +class HardwareWallet { + + // Public + + // Constructor + constructor(application) { + + // Set application + this.application = application; + + // Set transport to no transport + this.transport = HardwareWallet.NO_TRANSPORT; + + // Set root public key to no root public key + this.rootPublicKey = HardwareWallet.NO_ROOT_PUBLIC_KEY; + + // Set seed cookie to no seed cookie + this.seedCookie = HardwareWallet.NO_SEED_COOKIE; + + // Set connected to false + this.connected = false; + + // Set in use to false + this.inUse = false; + + // Set exclusive lock obtained to false + this.exclusiveLockObtained = false; + + // Set exclusive lock release event index + this.exclusiveLockReleaseEventIndex = 0; + + // Set locked to false + this.locked = false; + + // Set connection type to USB connection type + this.connectionType = HardwareWallet.USB_CONNECTION_TYPE; + + // Set wallet key path + this.walletKeyPath = Wallet.NO_KEY_PATH; + + // Set hardware type + this.hardwareType = HardwareWallet.LEDGER_HARDWARE_TYPE; + } + + // Get root public key + getRootPublicKey() { + + // Return copy of root public key + return new Uint8Array(this.rootPublicKey); + } + + // Get seed cookie + getSeedCookie() { + + // Return seed cookie + return this.seedCookie; + } + + // Get connection type + getConnectionType() { + + // Return connection type + return this.connectionType; + } + + // Get hardware type + getHardwareType() { + + // Return hardware type + return this.hardwareType; + } + + // Is connected + isConnected() { + + // Return if connected + return this.connected === true; + } + + // Set in use + setInUse(inUse) { + + // Set is in use + this.inUse = inUse; + } + + // Get in use + getInUse() { + + // Return in use + return this.inUse; + } + + // Is locked + isLocked() { + + // Return if locked + return this.locked === true; + } + + // Close + close() { + + // Check if root public key exists + if(this.rootPublicKey !== HardwareWallet.NO_ROOT_PUBLIC_KEY) { + + // Securely clear the root public key + this.rootPublicKey.fill(0); + + // Set root public key to no root public key + this.rootPublicKey = HardwareWallet.NO_ROOT_PUBLIC_KEY; + } + + // Set seed cookie to no seed cookie + this.seedCookie = HardwareWallet.NO_SEED_COOKIE; + + // Clear in use + this.inUse = false; + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + } + }); + }); + } + + // Connect + connect(hardwareWalletDescriptor, failOnLock = false, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if not connected + if(self.isConnected() === false) { + + // Function get hardware wallet transport + var getHardwareWalletTransport = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if connecting to any hardware wallet descriptor + if(hardwareWalletDescriptor === HardwareWallet.ANY_HARDWARE_WALLET_DESCRIPTOR) { + + // Check if USB is supported + if("usb" in navigator === true) { + + // Return connecting to any USB hardware wallet + return HardwareWalletUsbTransport.request().then(function(transport) { + + // Set connection type to USB connection type + self.connectionType = HardwareWallet.USB_CONNECTION_TYPE; + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Check if error is that user canceled action + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NOT_FOUND_ERROR_CODE) || ("name" in error === true && error["name"] === "NotFoundError"))) { + + // Check if Bluetooth is supported + if("bluetooth" in navigator === true) { + + // Return connecting to any Bluetooth hardware wallet + return HardwareWalletBluetoothTransport.request().then(function(transport) { + + // Set connection type to Bluetooth connection type + self.connectionType = HardwareWallet.BLUETOOTH_CONNECTION_TYPE; + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Check if error is that user canceled action + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NOT_FOUND_ERROR_CODE) || ("name" in error === true && error["name"] === "NotFoundError"))) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('No hardware wallet was selected.')) + ((Common.isPopup() === true) ? Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.')) : "")); + } + + // Otherwise check if error was a connection error + else if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NETWORK_ERROR_CODE) || ("name" in error === true && error["name"] === "NetworkError"))) { + + // Check if is an extension + if(Common.isExtension() === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this extension can connect to it.'))); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this app can connect to it.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this site can connect to it.'))); + } + } + + // Otherwise check if error was an invalid state error + else if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.INVALID_STATE_ERROR_CODE) || ("name" in error === true && error["name"] === "InvalidStateError"))) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet is currently in use.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.'))); + } + }); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('No hardware wallet was selected.')) + ((Common.isPopup() === true) ? Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.')) : "")); + } + } + + // Otherwise check if error was an invalid state error + else if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.INVALID_STATE_ERROR_CODE) || ("name" in error === true && error["name"] === "InvalidStateError"))) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet is currently in use.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.'))); + } + }); + } + + // Otherwise check if Bluetooth is supported + else if("bluetooth" in navigator === true) { + + // Return connecting to any Bluetooth hardware wallet + return HardwareWalletBluetoothTransport.request().then(function(transport) { + + // Set connection type to Bluetooth connection type + self.connectionType = HardwareWallet.BLUETOOTH_CONNECTION_TYPE; + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Check if error is that user canceled action + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NOT_FOUND_ERROR_CODE) || ("name" in error === true && error["name"] === "NotFoundError"))) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('No hardware wallet was selected.')) + ((Common.isPopup() === true) ? Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to open this extension in a tab or window if it\'s not able to connect to a hardware wallet.')) : "")); + } + + // Otherwise check if error was a connection error + else if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NETWORK_ERROR_CODE) || ("name" in error === true && error["name"] === "NetworkError"))) { + + // Check if is an extension + if(Common.isExtension() === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this extension can connect to it.'))); + } + + // Otherwise check if is an app + else if(Common.isApp() === true) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this app can connect to it.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.')) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to be already paired with the device before this site can connect to it.'))); + } + } + + // Otherwise check if error was an invalid state error + else if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.INVALID_STATE_ERROR_CODE) || ("name" in error === true && error["name"] === "InvalidStateError"))) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet is currently in use.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.'))); + } + }); + } + } + + // Otherwise + else { + + // Return connecting to provided hardware wallet descriptor + return HardwareWalletUsbTransport.request(hardwareWalletDescriptor).then(function(transport) { + + // Set connection type to USB connection type + self.connectionType = HardwareWallet.USB_CONNECTION_TYPE; + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Connecting to that hardware wallet failed.'))); + }); + } + }); + }; + + // Return gwetting hardware wallet transport + return getHardwareWalletTransport().then(function(transport) { + + // Set transport + self.transport = transport; + + // Get product name + var productName = self.transport["deviceModel"]["productName"]; + + // Get minimum compatible version + var minimumCompatibleVersion = self.getMinimumCompatibleVersion(); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Set hardware type to Ledger hardware type + self.hardwareType = HardwareWallet.LEDGER_HARDWARE_TYPE; + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Set hardware type to Trezor hardware type + self.hardwareType = HardwareWallet.TREZOR_HARDWARE_TYPE; + + // Break + break; + } + + // Set connected + self.connected = true; + + // Transport on disconnect + self.transport.on("disconnect", function() { + + // Obtain exclusive lock + self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + } + + // Release exclusive lock + self.releaseExclusiveLock(); + }); + }); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Return requesting getting the application information from the hardware wallet + return self.send(HardwareWalletDefinitions.LEDGER_GET_APPLICATION_INFORMATION_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred).then(function(response) { + + // Check if response contains an application name length + if(response["length"] >= Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]) { + + // Get application name length + var applicationNameLength = response[Uint8Array["BYTES_PER_ELEMENT"]]; + + // Check if response contains an application name + if(response["length"] >= Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength) { + + // Set error occured to false + var errorOccurred = false; + + // Try + try { + // Get application name from the response + var applicationName = (new TextDecoder("utf-8", {"fatal": true})).decode(response.subarray(Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"], Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength)); + } + + // Catch error + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if error didn't occur and application name is valid + if(errorOccurred === false && applicationName === HardwareWallet.APPLICATION_NAME) { + + // Check if response contains an application version length + if(response["length"] >= Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength + Uint8Array["BYTES_PER_ELEMENT"]) { + + // Get application version length + var applicationVersionLength = response[Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength]; + + // Check if response contains an application version + if(response["length"] >= Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength + Uint8Array["BYTES_PER_ELEMENT"] + applicationVersionLength) { + + // Try + try { + + // Get application version from the response + var applicationVersion = (new TextDecoder("utf-8", {"fatal": true})).decode(response.subarray(Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength + Uint8Array["BYTES_PER_ELEMENT"], Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + applicationNameLength + Uint8Array["BYTES_PER_ELEMENT"] + applicationVersionLength)); + } + + // Catch error + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if error didn't occur and application version is compatible + if(errorOccurred === false && HardwareWallet.isCompatibleVersion(applicationVersion, minimumCompatibleVersion) === true) { + + // Return requesting getting the seed cookie from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_SEED_COOKIE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT) + + }, HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === HardwareWallet.SEED_COOKIE_LENGTH) { + + // Get seed cookie from response + self.seedCookie = response; + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the seed cookie from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + + // Catch errors + }).catch(function(error) { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if error is canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if error is disconnected error + else if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the seed cookie from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + } + }); + }); + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The app on that %1$x hardware wallet isn\'t compatible. Update the app on the hardware wallet to version %2$v or newer to continue.'), [productName, minimumCompatibleVersion])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + }); + } + + // Catch errors + }).catch(function(error) { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if error is canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if error is disconnected error + else if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the app information from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText((failOnLock === false) ? Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet.') : Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + } + }); + }); + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Return requesting initializing on the hardware wallet + return self.send(HardwareWalletDefinitions.TREZOR_INITIALIZE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, HardwareWalletDefinitions.TREZOR_FEATURES_MESSAGE_TYPE, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred).then(function(response) { + + // Check if response contains a major version, minor version, patch version, pin protection, passphrase protection, initialized, unlocked, and model + if(response["length"] >= Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]) { + + // Get major version + var majorVersion = parseInt(Common.toHexString(response.subarray(0, Uint32Array["BYTES_PER_ELEMENT"])), Common.HEX_NUMBER_BASE); + + // Get minor version + var minorVersion = parseInt(Common.toHexString(response.subarray(Uint32Array["BYTES_PER_ELEMENT"], Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"])), Common.HEX_NUMBER_BASE); + + // Get patch version + var patchVersion = parseInt(Common.toHexString(response.subarray(Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"], Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"])), Common.HEX_NUMBER_BASE); + + // Get firmware version + var firmwareVersion = majorVersion.toFixed() + "." + minorVersion.toFixed() + "." + patchVersion.toFixed(); + + // Get model length + var modelLength = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]]; + + // Check if response contains a model and capabilities + if(response["length"] >= Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength + Uint8Array["BYTES_PER_ELEMENT"]) { + + // Set error occured to false + var errorOccurred = false; + + // Try + try { + + // Get model + var model = (new TextDecoder("utf-8", {"fatal": true})).decode(response.subarray(Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"], Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength)); + } + + // Catch error + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if error didn't occur + if(errorOccurred === false) { + + // Check model + switch(model) { + + // One + case "1": + + // Update transport's product name to include model + self.transport["deviceModel"]["productName"] += " Model One"; + + // Break + break; + + // T + case "T": + + // Update transport's product name to include model + self.transport["deviceModel"]["productName"] += " Model T"; + + // Break + break; + + // Safe 3, Safe 5, or default + case "Safe 3": + case "Safe 5": + default: + + // Update transport's product name to include model + self.transport["deviceModel"]["productName"] += " " + model; + + // Break + break; + } + + // Update product name + productName = self.transport["deviceModel"]["productName"]; + + // Update minimum compatible version + minimumCompatibleVersion = self.getMinimumCompatibleVersion(); + + // Check if firmware version is compatible + if(HardwareWallet.isCompatibleVersion(firmwareVersion, minimumCompatibleVersion) === true) { + + // Get capabilities length + var capabilitiesLength = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength]; + + // Check if response contains capabilities and passphrase always on device + if(response["length"] >= Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength + Uint8Array["BYTES_PER_ELEMENT"] + capabilitiesLength + Uint8Array["BYTES_PER_ELEMENT"]) { + + // Get capabilities + var capabilities = response.subarray(Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength + Uint8Array["BYTES_PER_ELEMENT"], Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength + Uint8Array["BYTES_PER_ELEMENT"] + capabilitiesLength); + + // Check if device is MimbleWimble Coin capable + if(capabilities.indexOf(HardwareWallet.MIMBLEWIMBLE_COIN_CAPABLE) !== Common.INDEX_NOT_FOUND) { + + // Get initialized + var initialized = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]] !== 0; + + // Check if device is initialized + if(initialized === true) { + + // Get pin enabled + var pinEnabled = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"]] !== 0; + + // Get unlocked + var unlocked = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]] !== 0; + + // Get passphrase enabled + var passphraseEnabled = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]] !== 0; + + // Get passphrase always on device + var passphraseAlwaysOnDevice = response[Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint32Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + modelLength + Uint8Array["BYTES_PER_ELEMENT"] + capabilitiesLength] !== 0; + + // Check if not set to fail on lock, pin isn't enabled, or device is unlocked + if(failOnLock === false || pinEnabled === false || unlocked === true) { + + // Check if not set to fail on lock, passphrase isn't enabled, or passphrase always on device isn't enabled + if(failOnLock === false || passphraseEnabled === false || passphraseAlwaysOnDevice === false) { + + // Return requesting getting the seed cookie from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_SEED_COOKIE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT) + + }, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_SEED_COOKIE_MESSAGE_TYPE, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === HardwareWallet.SEED_COOKIE_LENGTH) { + + // Get seed cookie from response + self.seedCookie = response; + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the seed cookie from that %1$x hardware wallet failed.'), [productName])); + }); + } + + // Catch errors + }).catch(function(error) { + + // Return closing transport and catch errors + return self.transport.close().catch(function() { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if error is canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if error is disconnected error + else if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + + // Otherwise + else { + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"], HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the seed cookie from that %1$x hardware wallet failed.'), [productName])); + } + }); + }); + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That %1$x hardware wallet is locked. Unlock the hardware wallet to continue.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That %1$x hardware wallet isn\'t initialized. Initialize the hardware wallet to continue.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The firmware on that %1$x hardware wallet isn\'t compatible. Install a compatible firmware on the hardware wallet to continue.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the features from that %1$x hardware wallet failed.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The firmware on that %1$x hardware wallet isn\'t compatible. Update the firmware on the hardware wallet to version %2$v or newer to continue.'), [productName, minimumCompatibleVersion])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the features from that %1$x hardware wallet failed.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the features from that %1$x hardware wallet failed.'), [productName])); + }); + } + } + + // Otherwise + else { + + // Return closing transport and catch errors + return self.transport.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the features from that %1$x hardware wallet failed.'), [productName])); + }); + } + + // Catch errors + }).catch(function(error) { + + // Return closing transport and catch errors + return self.transport.close().catch(function() { + + // Finally + }).finally(function() { + + // Clear connected + self.connected = false; + + // Set transport to no transport + self.transport = HardwareWallet.NO_TRANSPORT; + + // Trigger disconnect event + $(self).trigger(HardwareWallet.DISCONNECT_EVENT); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if error is canceled + if(error === Common.CANCELED_ERROR) { + + // Reject error + reject(error); + } + + // Otherwise check if error is disconnected error + else if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + + // Otherwise + else { + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"], HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the features from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('You may need to disconnect and reconnect the hardware wallet to connect to it.'))); + } + }); + }); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('The hardware wallet is already connected.'))); + } + }); + }); + } + + // Get public key + getPublicKey() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Get product name + var productName = self.transport["deviceModel"]["productName"]; + + // Return requesting getting the root public key from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ROOT_PUBLIC_KEY_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ROOT_PUBLIC_KEY_MESSAGE_TYPE], HardwareWallet.NO_TEXT, [], false, true, true, Common.NO_CANCEL_OCCURRED).then(function(response) { + + // Check if response is valid + if(response["length"] === Crypto.SECP256K1_PUBLIC_KEY_LENGTH && Secp256k1Zkp.isValidPublicKey(response) === true) { + + // Get root public key from response + self.rootPublicKey = response; + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve root public key + resolve(self.getRootPublicKey()); + } + + // Otherwise + else { + + // Securely clear response + response.fill(0); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the root public key from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the root public key from that %1$x hardware wallet failed.'), [productName])); + + // Break + break; + } + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if error is disconnected error + if(error === HardwareWallet.DISCONNECTED_ERROR) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + + // Otherwise + else { + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Check if user rejected the request + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.LEDGER_USER_REJECTED_MESSAGE_TYPE) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Exporting the root public key on that %1$x hardware wallet was denied.'), [productName])); + } + + // Otherwise + else { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the root public key from that %1$x hardware wallet failed.'), [productName]) + Message.createText(Language.getDefaultTranslation('(?<=.) ')) + Message.createText(Language.getDefaultTranslation('Make sure that the correct app is open on the hardware wallet and that the hardware wallet isn\'t locked.'))); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Exporting the root public key on that %1$x hardware wallet was denied.'), [productName])); + + // Return + return; + } + } + } + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('Getting the root public key from that %1$x hardware wallet failed.'), [productName])); + + // Break + break; + } + } + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(Message.createText(Language.getDefaultTranslation('That hardware wallet was disconnected.'))); + } + }); + }); + } + + // Get commit + getCommit(value, identifier, switchType, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting getting the commit from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_COMMITMENT_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Identifier + "Identifier": identifier.getValue(), + + // Value + "Value": value, + + // Switch type + "Switch Type": switchType + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_COMMITMENT_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === Crypto.COMMIT_LENGTH && Secp256k1Zkp.isValidCommit(response) === true) { + + // Get commit from response + var commit = response; + + // Resolve commit + resolve(commit); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Get proof + getProof(value, identifier, switchType, message, proofBuilder, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting getting the commit from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_COMMITMENT_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Identifier + "Identifier": identifier.getValue(), + + // Value + "Value": value, + + // Switch type + "Switch Type": switchType + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_COMMITMENT_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === Crypto.COMMIT_LENGTH && Secp256k1Zkp.isValidCommit(response) === true) { + + // Get commit from response + var commit = response; + + // Return getting rewind nonce from the proof builder + return proofBuilder.rewindNonce(commit).then(function(rewindNonce) { + + // Try + try { + + // Return requesting getting the proof components from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_BULLETPROOF_COMPONENTS_MESSAGE_TYPE, message, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Message type + "Parameter One": message, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Identifier + "Identifier": identifier.getValue(), + + // Value + "Value": value, + + // Switch type + "Switch Type": switchType + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_BULLETPROOF_COMPONENTS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH && (Secp256k1Zkp.isValidSecretKey(response.subarray(0, Crypto.TAU_X_LENGTH)) === true || Common.arraysAreEqual(response.subarray(0, Crypto.TAU_X_LENGTH), Crypto.ZERO_SECRET_KEY) === true) && Secp256k1Zkp.isValidPublicKey(response.subarray(Crypto.TAU_X_LENGTH, Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH)) === true && Secp256k1Zkp.isValidPublicKey(response.subarray(Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH, Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH)) === true) { + + // Get tau x from response + var tauX = response.subarray(0, Crypto.TAU_X_LENGTH); + + // Get t one from response + var tOne = response.subarray(Crypto.TAU_X_LENGTH, Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH); + + // Get t two from response + var tTwo = response.subarray(Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH, Crypto.TAU_X_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH + Crypto.SECP256K1_PUBLIC_KEY_LENGTH); + + // Try + try { + + // Get proof message from identifier and switch type + var proofMessage = proofBuilder.proofMessage(identifier, switchType); + } + + // Catch errors + catch(error) { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Reject error + reject(error); + + // Return + return; + } + + // Check if creating proof with the tau x, t one, t two, commit, value, rewind nonce, and proof message was successful + var proof = Secp256k1Zkp.createBulletproofBlindless(tauX, tOne, tTwo, commit, value.toFixed(), rewindNonce, new Uint8Array([]), proofMessage); + + if(proof !== Secp256k1Zkp.OPERATION_FAILED && Secp256k1Zkp.verifyBulletproof(proof, commit, new Uint8Array([])) === true) { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Resolve proof + resolve(proof); + } + + // Otherwise + else { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Securely clear rewind nonce + rewindNonce.fill(0); + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Get Tor address + getTorAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting getting the Tor address from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE, HardwareWallet.TOR_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.TOR_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ADDRESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === ((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? Uint8Array["BYTES_PER_ELEMENT"] : 0) + Tor.ADDRESS_LENGTH) { + + // Try + try { + + // Get Tor address from response + var torAddress = (new TextDecoder("utf-8", {"fatal": true})).decode((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? response.subarray(Uint8Array["BYTES_PER_ELEMENT"]) : response); + + // Get public key from Tor address + Tor.torAddressToPublicKey(torAddress); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + + // Return + return; + } + + // Resolve Tor address + resolve(torAddress); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Verify Tor address + verifyTorAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting verifying the Tor address on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_VERIFY_ADDRESS_MESSAGE_TYPE, HardwareWallet.TOR_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.TOR_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Check if user rejected the request + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.LEDGER_USER_REJECTED_MESSAGE_TYPE) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(error); + + // Break + break; + } + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Get MQS address + getMqsAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting getting the MQS address from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE, HardwareWallet.MQS_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.MQS_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ADDRESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === ((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? Uint8Array["BYTES_PER_ELEMENT"] : 0) + Mqs.ADDRESS_LENGTH) { + + // Try + try { + + // Get MQS address from response + var mqsAddress = (new TextDecoder("utf-8", {"fatal": true})).decode((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? response.subarray(Uint8Array["BYTES_PER_ELEMENT"]) : response); + + // Get public key from MQS address + Mqs.mqsAddressToPublicKey(mqsAddress, Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + + // Return + return; + } + + // Resolve MQS address + resolve(mqsAddress); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Verify MQS address + verifyMqsAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting verifying the MQS address on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_VERIFY_ADDRESS_MESSAGE_TYPE, HardwareWallet.MQS_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.MQS_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Check if user rejected the request + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.LEDGER_USER_REJECTED_MESSAGE_TYPE) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(error); + + // Break + break; + } + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Get Slatepack address + getSlatepackAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting getting the Slatepack address from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE, HardwareWallet.SLATEPACK_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.SLATEPACK_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ADDRESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === ((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? Uint8Array["BYTES_PER_ELEMENT"] : 0) + Slatepack.ADDRESS_LENGTH) { + + // Try + try { + + // Get Slatepack address from response + var slatepackAddress = (new TextDecoder("utf-8", {"fatal": true})).decode((self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE) ? response.subarray(Uint8Array["BYTES_PER_ELEMENT"]) : response); + + // Get public key from Slatepack address + Slatepack.slatepackAddressToPublicKey(slatepackAddress); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + + // Return + return; + } + + // Resolve Slatepack address + resolve(slatepackAddress); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Verify Slatepack address + verifySlatepackAddress(index, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting verifying the Slatepack address on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_VERIFY_ADDRESS_MESSAGE_TYPE, HardwareWallet.SLATEPACK_ADDRESS_TYPE, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Address type + "Parameter One": HardwareWallet.SLATEPACK_ADDRESS_TYPE, + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Check if user rejected the request + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.LEDGER_USER_REJECTED_MESSAGE_TYPE) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(error); + + // Break + break; + } + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Decrypt slate + decryptSlate(index, slate, address, nonce, salt = HardwareWallet.NO_SALT, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Check if slate is valid + if(slate["length"] > Slatepack.TAG_LENGTH) { + + // Try + try { + + // Return requesting start decrypting on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_DECRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index), + + // Nonce + "Nonce": nonce, + + // Sender address or ephemeral X25519 public key + "Sender Address Or Ephemeral X25519 Public Key": (new TextEncoder()).encode(address), + + // Salt or encrypted file key + "Salt Or Encrypted File Key": (salt !== HardwareWallet.NO_SALT) ? salt : new Uint8Array([]), + + // Payload nonce + "Payload Nonce": new Uint8Array([]) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === 0) { + + // Get encrypted data from slate + var encryptedData = slate.subarray(0, slate["length"] - Slatepack.TAG_LENGTH); + + // Set decrypt chunk + var decryptChunk = new Promise(function(resolve, reject) { + + // Resolve + resolve(); + }); + + // Initialize decrypting chunks + var decryptingChunks = [decryptChunk]; + + // Go through all of the encrypted data chunks + for(let i = 0; i < Math.ceil(encryptedData["length"] / HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE); ++i) { + + // Decrypt chunk after the previous chunk is decrypted + decryptChunk = decryptChunk.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return decrypting chunk on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_DECRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Data + "Encrypted Data": encryptedData.subarray(i * HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE, Math.min(encryptedData["length"], i * HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE + HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE)) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_DATA_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] > 0) { + + // Get decrypted data chunk from response + var decryptedDataChunk = response; + + // Resolve decrypted data chunk + resolve(decryptedDataChunk); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Return Promise + return new Promise(function(resolve, reject) { + + // Reject error + reject(error); + }); + }); + + // Append decrypting chunk to list + decryptingChunks.push(decryptChunk); + } + + // Return decrypting all chunks + return Promise.all(decryptingChunks).then(function(decryptedDataChunks) { + + // Get tag from slate + var tag = slate.subarray(slate["length"] - Slatepack.TAG_LENGTH); + + // Return requesting finish decrypting on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_DECRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Tag + "Tag": tag + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_AES_KEY_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] === Crypto.AES_KEY_LENGTH) { + + // Get AES key from response + var aesKey = response; + + // Initialize AES decrypt chunks + var aesDecryptChunks = []; + + // Go through all decrypted chunks + for(var i = 1; i < decryptedDataChunks["length"]; ++i) { + + // Get decrypted data chunk + let decryptedDataChunk = decryptedDataChunks[i]; + + // Append decrypting AES chunk to list + aesDecryptChunks.push(new Promise(function(resolve, reject) { + + // Return performing AES decryption on decrypted data chunk using the AES key + return Crypto.aesDecrypt(decryptedDataChunk, aesKey).then(function(dataChunk) { + + // Resolve data chunk + resolve(dataChunk); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + })); + } + + // Return waiting for all data chunks to be AES decrypted + return Promise.all(aesDecryptChunks).then(function(dataChunks) { + + // Securely clear AES key + aesKey.fill(0); + + // Resolve combined data chunks + resolve(Common.mergeArrays(dataChunks)); + + // Catch errors + }).catch(function(error) { + + // Securely clear AES key + aesKey.fill(0); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Encrypt slate + encryptSlate(index, slate, address, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Check if slate is valid + if(slate["length"] !== 0) { + + // Try + try { + + // Return requesting start encrypting on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_ENCRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index), + + // Recipient address + "Recipient Address": (new TextEncoder()).encode(address) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_NONCE_AND_SALT_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] >= Slatepack.NONCE_LENGTH) { + + // Get nonce from response + var nonce = response.subarray(0, Slatepack.NONCE_LENGTH); + + // Get decrypted data from slate + var decryptedData = slate; + + // Set encrypt chunk + var encryptChunk = new Promise(function(resolve, reject) { + + // Resolve + resolve(new Uint8Array([])); + }); + + // Initialize encrypting chunks + var encryptingChunks = [encryptChunk]; + + // Go through all of the decrypted data chunks + for(let i = 0; i < Math.ceil(decryptedData["length"] / HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE); ++i) { + + // Encrypt chunk after the previous chunk is encrypted + encryptChunk = encryptChunk.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return encrypting chunk on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_ENCRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Data + "Data": decryptedData.subarray(i * HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE, Math.min(decryptedData["length"], i * HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE + HardwareWallet.ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE)) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_DATA_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] > 0) { + + // Get encrypted data chunk from response + var encryptedDataChunk = response; + + // Resolve encrypted data chunk + resolve(encryptedDataChunk); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Return Promise + return new Promise(function(resolve, reject) { + + // Reject error + reject(error); + }); + }); + + // Append encrypting chunk to list + encryptingChunks.push(encryptChunk); + } + + // Return encrypting all chunks + return Promise.all(encryptingChunks).then(function(encryptedDataChunks) { + + // Return requesting finish encrypting on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_ENCRYPTING_SLATE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_TAG_AND_SIGNATURE_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check if response is valid + if(response["length"] >= Slatepack.TAG_LENGTH) { + + // Get tag from response + var tag = response.subarray(0, Slatepack.TAG_LENGTH); + + // Append the tag to the encrypted data chunks + var encryptedData = Common.mergeArrays([ + + // Encrypted data chunks + Common.mergeArrays(encryptedDataChunks), + + // tag + tag + ]); + + // Resolve nonce and encrypted data + resolve([ + + // Nonce + nonce, + + // Encrypted data + encryptedData + ]); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Start transaction + startTransaction(index, output, input, fee, secretNonceIndex = HardwareWallet.NO_SECRET_NONCE_INDEX, address = HardwareWallet.NO_ADDRESS, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return obtainined exclusive lock + return self.obtainExclusiveLock().then(function() { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting starting transaction on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_TRANSACTION_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Coin type + "Coin Type": Consensus.getWalletType(), + + // Network type + "Network Type": Consensus.getNetworkType(), + + // Account + "Account": new BigNumber(HardwareWallet.ACCOUNT), + + // Index + "Index": new BigNumber(index), + + // Output + "Output": output, + + // Input + "Input": input, + + // Fee + "Fee": fee, + + // Secret nonce index + "Secret Nonce Index": secretNonceIndex, + + // Address + "Address": (address !== HardwareWallet.NO_ADDRESS) ? (new TextEncoder()).encode(address) : new Uint8Array([]) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch error + catch(error) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + }); + }); + } + + // Include output in transaction + includeOutputInTransaction(value, identifier, switchType, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting including output in transaction on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_OUTPUT_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Identifier + "Identifier": identifier.getValue(), + + // Value + "Value": value, + + // Switch type + "Switch Type": switchType + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Include input in transaction + includeInputInTransaction(value, identifier, switchType, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Return requesting including input in transaction on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_INPUT_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Identifier + "Identifier": identifier.getValue(), + + // Value + "Value": value, + + // Switch type + "Switch Type": switchType + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === 0) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Apply offset to transaction + applyOffsetToTransaction(offset, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Return requesting applying an offset to the transaction on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_APPLY_OFFSET_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Offset + "Offset": offset + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_SECRET_NONCE_INDEX_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === 0 || response["length"] === 1) { + + // Get secret nonce index from response + var secretNonceIndex = (response["length"] === 1) ? response[0] : HardwareWallet.NO_SECRET_NONCE_INDEX; + + // Resolve secret nonce index + resolve(secretNonceIndex); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Get transaction public key + getTransactionPublicKey(text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Return requesting getting the transaction public key from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_KEY_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_KEY_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === Crypto.SECP256K1_PUBLIC_KEY_LENGTH && Secp256k1Zkp.isValidPublicKey(response) === true) { + + // Get public key from response + var publicKey = response; + + // Resolve public key + resolve(publicKey); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Get transaction public nonce + getTransactionPublicNonce(text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Return requesting getting the transaction public nonce from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_NONCE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_NONCE_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === Crypto.SECP256K1_PUBLIC_KEY_LENGTH && Secp256k1Zkp.isValidPublicKey(response) === true) { + + // Get public nonce from response + var publicNonce = response; + + // Resolve public nonce + resolve(publicNonce); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Get transaction message signature + getTransactionMessageSignature(message, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Return requesting getting the transaction message signature from the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_MESSAGE_SIGNATURE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Message + "Message": (new TextEncoder()).encode(message) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_MESSAGE_SIGNATURE_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] === Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH && Secp256k1Zkp.isValidSingleSignerSignature(response) === true) { + + // Get message signature from response + var messageSignature = response; + + // Resolve signature + resolve(messageSignature); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + + // Return + return; + } + } + } + + // Break + break; + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Get transaction information + getTransactionInformation(publicNonce, publicKey, features, lockHeight = Slate.NO_LOCK_HEIGHT, relativeHeight = Slate.NO_RELATIVE_HEIGHT, kernelCommit = HardwareWallet.NO_KERNEL_COMMIT, address = HardwareWallet.NO_ADDRESS, receiverSignature = Slate.NO_RECEIVER_SIGNATURE, text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Check if connected + if(self.isConnected() === true) { + + // Try + try { + + // Check features + switch(features) { + + // Coinbase or plain features + case SlateKernel.COINBASE_FEATURES: + case SlateKernel.PLAIN_FEATURES: + + // Set kernel information to features + var kernelInformation = new Uint8Array([features]); + + // Break + break; + + // Height locked features + case SlateKernel.HEIGHT_LOCKED_FEATURES: + + // Set kernel information to features followed by the lock height + var kernelInformation = Common.mergeArrays([ + + // Features + new Uint8Array([features]), + + // Lock height + lockHeight.toBytes(BigNumber.LITTLE_ENDIAN, Common.BYTES_IN_A_UINT64) + ]); + + // Break + break; + + // No recent duplicate features + case SlateKernel.NO_RECENT_DUPLICATE_FEATURES: + + // Set kernel features to features followed by the relative height + var kernelInformation = Common.mergeArrays([ + + // Features + new Uint8Array([features]), + + // Relative height + relativeHeight.toBytes(BigNumber.LITTLE_ENDIAN, Common.BYTES_IN_A_UINT16) + ]); + + // Break + break; + } + } + + // Catch errors + catch(error) { + + // Reject error + reject(error); + + // Return + return; + } + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check if address exists + if(address !== HardwareWallet.NO_ADDRESS) { + + // Check address length + switch(address["length"]) { + + // Tor address length + case Tor.ADDRESS_LENGTH: + + // Set address type + var addressType = HardwareWallet.TOR_ADDRESS_TYPE; + + // Break + break; + + // MQS address length + case Mqs.ADDRESS_LENGTH: + + // Set address type + var addressType = HardwareWallet.MQS_ADDRESS_TYPE; + + // Break + break; + } + } + + // Otherwise + else { + + // Set address type + var addressType = HardwareWallet.MQS_ADDRESS_TYPE; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Set address type + var addressType = HardwareWallet.SLATEPACK_ADDRESS_TYPE; + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Set address type + var addressType = HardwareWallet.TOR_ADDRESS_TYPE; + + // Break + break; + } + + // Return requesting finishing the transaction on the hardware wallet + return self.send(HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_TRANSACTION_MESSAGE_TYPE, addressType, HardwareWallet.NO_PARAMETER, { + + // Address type + "Parameter One": addressType, + + // Public nonce + "Public Nonce": publicNonce, + + // Public key + "Public Key": publicKey, + + // Kernel information + "Kernel Information": kernelInformation, + + // Kernel commitment + "Kernel Commitment": (kernelCommit !== HardwareWallet.NO_KERNEL_COMMIT) ? kernelCommit : new Uint8Array([]), + + // Payment proof + "Payment Proof": (receiverSignature !== Slate.NO_RECEIVER_SIGNATURE) ? receiverSignature : new Uint8Array([]) + + }, [HardwareWalletDefinitions.LEDGER_SUCCESS_MESSAGE_TYPE, HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_SIGNATURE_AND_PAYMENT_PROOF_MESSAGE_TYPE], text, textArguments, allowUnlock, false, preventMessages, cancelOccurred).then(function(response) { + + // Check if response is valid + if(response["length"] >= Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH && Secp256k1Zkp.isValidSingleSignerSignature(response.subarray(0, Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH)) === true) { + + // Get signature from response + var signature = response.subarray(0, Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH); + + // Get payment proof from response + var paymentProof = (response["length"] > Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH) ? response.subarray(Crypto.SINGLE_SIGNER_SIGNATURE_LENGTH) : HardwareWallet.NO_PAYMENT_PROOF; + + // Check if payment proof is valid + if(paymentProof === HardwareWallet.NO_PAYMENT_PROOF || paymentProof["length"] <= Crypto.MAXIMUM_MESSAGE_HASH_SIGNATURE_LENGTH || paymentProof["length"] === Crypto.ED25519_SIGNATURE_LENGTH) { + + // Resolve transaction information + resolve([ + + // Signature + signature, + + // Payment proof + paymentProof + ]); + } + + // Otherwise + else { + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check transport's type + switch(self.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Check if user rejected the request + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.LEDGER_USER_REJECTED_MESSAGE_TYPE) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Reject user rejected error + reject(HardwareWallet.USER_REJECTED_ERROR); + + // Return + return; + } + } + } + + // Reject error + reject(error); + + // Break + break; + } + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Cancel transaction + cancelTransaction() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Complete transaction + completeTransaction() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Release exclusive lock + self.releaseExclusiveLock(); + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject + reject(); + } + }); + } + + // Set wallet key path + setWalletKeyPath(walletKeyPath) { + + // Set wallet key path + this.walletKeyPath = walletKeyPath; + } + + // Get wallet key path + getWalletKeyPath() { + + // Return wallet key path + return this.walletKeyPath; + } + + // Get available hardware wallet descriptors + static getAvailableHardwareWalletDescriptors() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return getting available hardware wallet descriptors + return HardwareWalletUsbTransport.list().then(function(availableHardwareWalletDescriptors) { + + // Resolve available hardware wallet descriptors + resolve(availableHardwareWalletDescriptors); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Is supported + static isSupported() { + + // Return if USB or Bluetooth are supported + return "usb" in navigator === true || "bluetooth" in navigator === true; + } + + // Any hardware wallet descriptor + static get ANY_HARDWARE_WALLET_DESCRIPTOR() { + + // Return any hardware wallet descriptor + return null; + } + + // Ledger hardware type + static get LEDGER_HARDWARE_TYPE() { + + // Return Ledger hardware type + return 0; + } + + // Trezor hardware type + static get TREZOR_HARDWARE_TYPE() { + + // Return Trezor hardware type + return HardwareWallet.LEDGER_HARDWARE_TYPE + 1; + } + + // Disconnect event + static get DISCONNECT_EVENT() { + + // Return disconnect event + return "HardwareWalletDisconnectEvent"; + } + + // Before disconnect event + static get BEFORE_DISCONNECT_EVENT() { + + // Return before disconnect event + return "HardwareWalletBeforeDisconnectEvent"; + } + + // Unlock event + static get UNLOCK_EVENT() { + + // Return unlock event + return "HardwareWalletUnlockEvent"; + } + + // Device cancel event + static get DEVICE_CANCEL_EVENT() { + + // Return device cancel event + return "HardwareWalletDeviceCancelEvent"; + } + + // Transaction information signature index + static get TRANSACTION_INFORMATION_SIGNATURE_INDEX() { + + // Return transaction information signature index + return 0; + } + + // Transaction information payment proof index + static get TRANSACTION_INFORMATION_PAYMENT_PROOF_INDEX() { + + // Return transaction information payment proof index + return HardwareWallet.TRANSACTION_INFORMATION_SIGNATURE_INDEX + 1; + } + + // No text + static get NO_TEXT() { + + // Return no text + return null; + } + + // Disconnected error + static get DISCONNECTED_ERROR() { + + // Return disconnected error + return "HardwareWalletDisconnectedError"; + } + + // User rejected error + static get USER_REJECTED_ERROR() { + + // Return user rejected error + return "HardwareWalletUserRejectedError"; + } + + // Encrypted slate nonce index + static get ENCRYPTED_SLATE_NONCE_INDEX() { + + // Return encrypted slate nonce index + return 0; + } + + // Encrypted slate data index + static get ENCRYPTED_SLATE_DATA_INDEX() { + + // Return encrypted slate data index + return HardwareWallet.ENCRYPTED_SLATE_NONCE_INDEX + 1; + } + + // Sending transaction message + static get SENDING_TRANSACTION_MESSAGE() { + + // Return sending transaction message + return 0; + } + + // Receiving transaction message + static get RECEIVING_TRANSACTION_MESSAGE() { + + // Return receiving transaction message + return HardwareWallet.SENDING_TRANSACTION_MESSAGE + 1; + } + + // Creating coinbase message + static get CREATING_COINBASE_MESSAGE() { + + // Return creating coinbase message + return HardwareWallet.RECEIVING_TRANSACTION_MESSAGE + 1; + } + + // USB connection type + static get USB_CONNECTION_TYPE() { + + // Return USB connection type + return 0; + } + + // Bluetooth connection type + static get BLUETOOTH_CONNECTION_TYPE() { + + // Return Bluetooth connection type + return HardwareWallet.USB_CONNECTION_TYPE + 1; + } + + // No secret nonce index + static get NO_SECRET_NONCE_INDEX() { + + // Return no secret nonce index + return 0; + } + + // Private + + // Obtain exclusive lock + obtainExclusiveLock() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if exclusive lock is locked + if(self.exclusiveLockObtained === true) { + + // Get current exclusive lock release event index + var index = self.exclusiveLockReleaseEventIndex++; + + // Check if current current exclusive lock release event index is at the max safe integer + if(index === Number.MAX_SAFE_INTEGER) + + // Reset current exclusive lock release event index + self.exclusiveLockReleaseEventIndex = 0; + + // Exclusive lock release index event + $(self).on(HardwareWallet.EXCLUSIVE_LOCK_RELEASE_EVENT + "." + index.toFixed(), function() { + + // Check if exclusive lock isn't locked + if(self.exclusiveLockObtained === false) { + + // Turn off exclusive lock release index event + $(self).off(HardwareWallet.EXCLUSIVE_LOCK_RELEASE_EVENT + "." + index.toFixed()); + + // Lock exclusive lock + self.exclusiveLockObtained = true; + + // Resolve + resolve(); + } + }); + } + + // Otherwise + else { + + // Lock exclusive lock + self.exclusiveLockObtained = true; + + // Resolve + resolve(); + } + }); + } + + // Release exclusive lock + releaseExclusiveLock() { + + // Check if exclusive lock is locked + if(this.exclusiveLockObtained === true) { + + // Set self + var self = this; + + // Set timeout + setTimeout(function() { + + // Unlock exclusive lock + self.exclusiveLockObtained = false; + + // Trigger exclusive lock release event + $(self).trigger(HardwareWallet.EXCLUSIVE_LOCK_RELEASE_EVENT); + }, 0); + } + } + + // Send + send(messageType, parameterOne, parameterTwo, data = HardwareWallet.NO_DATA, allowedResponseTypes = [], text = HardwareWallet.NO_TEXT, textArguments = [], allowUnlock = false, failOnLock = false, preventMessages = false, cancelOccurred = Common.NO_CANCEL_OCCURRED, forceSend = false, preventUnlockMessageDone = false, unlockMessageShown = false) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if cancel didn't occur or force sending + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || forceSend === true) { + + // Return performing the instruction on the hardware wallet + return self.transport.send(messageType, parameterOne, parameterTwo, self.encode(messageType, data)).then(function(response) { + + // Check if cancel didn't occur or force sending and unlock message isn't shown + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false || (forceSend === true && unlockMessageShown === false)) { + + // Check if the hardware wallet is locked and not set to fail on lock + if((self.transport.type === HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE && (response["Message Type"] === HardwareWalletDefinitions.LEDGER_APP_LOCKED_MESSAGE_TYPE || response["Message Type"] === HardwareWalletDefinitions.LEDGER_DEVICE_LOCKED_MESSAGE_TYPE)) && failOnLock === false) { + + // Set locked + self.locked = true; + + // Initialize canceled + var canceled = false; + + // Check if showing message + if(text !== HardwareWallet.NO_TEXT) { + + // Show hardware wallet unlock message + var showMessage = self.application.showHardwareWalletUnlockMessage(self, text, textArguments, allowUnlock, preventMessages, cancelOccurred); + + // Catch errors while showing the message + showMessage.catch(function(error) { + + // Set canceled + canceled = true; + }); + } + + // Resend + var resend = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Set timeout + setTimeout(function() { + + // Check if canceled + if(canceled === true) { + + // Clear locked + self.locked = false; + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Return performing the instruction on the hardware wallet + return self.transport.send(messageType, parameterOne, parameterTwo, self.encode(messageType, data)).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if the hardware wallet is locked and not set to fail on lock + if((self.transport.type === HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE && (response["Message Type"] === HardwareWalletDefinitions.LEDGER_APP_LOCKED_MESSAGE_TYPE || response["Message Type"] === HardwareWalletDefinitions.LEDGER_DEVICE_LOCKED_MESSAGE_TYPE)) && failOnLock === false) { + + // Return resend + return resend().then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch error + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if response type isn't allowed + else if(response["Message Type"] !== ((Array.isArray(allowedResponseTypes) === true) ? allowedResponseTypes[self.transport.type] : allowedResponseTypes)) { + + // Clear locked + self.locked = false; + + // Check if response data isn't used + if(response["Message Type"] !== HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Securely clear response data + response["Data"].fill(0); + } + + // Reject response + reject(response); + } + + // Otherwise + else { + + // Clear locked + self.locked = false; + + // Try + try { + + // Decode response + var decodedResponse = self.decode(response["Message Type"], response["Data"]); + } + + // Catch errors + catch(error) { + + // Securely clear response data + response["Data"].fill(0); + + // Reject error + reject(error); + + // Return + return; + } + + // Securely clear response data + response["Data"].fill(0); + + // Trigger unlock event + $(self).trigger(HardwareWallet.UNLOCK_EVENT); + + // Check if showing message and not canceled + if(text !== HardwareWallet.NO_TEXT && canceled === false) { + + // Return waiting until showing message has finished + return showMessage.then(function() { + + // Resolve decoded response + resolve(decodedResponse); + + // Catch errors + }).catch(function() { + + // Resolve decoded response + resolve(decodedResponse); + }); + } + + // Otherwise + else { + + // Resolve decoded response + resolve(decodedResponse); + } + } + } + + // Otherwise + else { + + // Securely clear response data + response["Data"].fill(0); + + // Clear locked + self.locked = false; + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if error is that the device was disconnected + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NETWORK_ERROR_CODE) || ("name" in error === true && error["name"] === "NetworkError"))) { + + // Clear locked + self.locked = false; + + // Trigger before disconnect event + $(self).trigger(HardwareWallet.BEFORE_DISCONNECT_EVENT); + + // Check if showing message and not canceled + if(text !== HardwareWallet.NO_TEXT && canceled === false) { + + // Return waiting until showing message has finished + return showMessage.then(function() { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + + // Catch errors + }).catch(function() { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Clear locked + self.locked = false; + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Clear locked + self.locked = false; + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Clear locked + self.locked = false; + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }, HardwareWallet.RESEND_REQUEST_DELAY_MILLISECONDS); + }); + }; + + // Return resend + return resend().then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch error + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if hardware wallet requires button acknowledgment + else if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_BUTTON_REQUEST_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode response + var decodedResponse = self.decode(response["Message Type"], response["Data"]); + } + + // Catch errors + catch(error) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if device is locked or needs a passphrase and not set to fail on lock + if(decodedResponse["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedResponse[0] === HardwareWalletDefinitions.TREZOR_PIN_ENTRY_BUTTON_REQUEST_TYPE || decodedResponse[0] === HardwareWalletDefinitions.TREZOR_PASSPHRASE_ENTRY_BUTTON_REQUEST_TYPE) && failOnLock === false) { + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Set locked + self.locked = true; + } + + // Initialize canceled + var canceled = false; + + // Initialize prevent cancel + var preventCancel = false; + + // Check if showing message and unlock message isn't shown + if(text !== HardwareWallet.NO_TEXT && unlockMessageShown === false) { + + // Show hardware wallet unlock message + var showMessage = self.application.showHardwareWalletUnlockMessage(self, text, textArguments, allowUnlock, preventMessages, cancelOccurred); + + // Catch errors while showing the message + showMessage.catch(function(error) { + + // Check if not preventing cancel + if(preventCancel === false) { + + // Set canceled + canceled = true; + + // Clear locked + self.locked = false; + + // Reset transport device and catch errors + self.transport.device.reset().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + }); + } + + // Return sending button acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_BUTTON_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, function() { + + // Return if cancel occurred or canceled + return (cancelOccurred !== Common.NO_CANCEL_OCCURRED && cancelOccurred() === true) || canceled === true; + + }, true, preventUnlockMessageDone, true).then(function(response) { + + // Check if not canceled + if(canceled === false) { + + // Set prevent cancel + preventCancel = true; + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Clear locked + self.locked = false; + } + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Trigger unlock event + $(self).trigger(HardwareWallet.UNLOCK_EVENT); + } + + // Check if showing message and unlock message isn't shown + if(text !== HardwareWallet.NO_TEXT && unlockMessageShown === false) { + + // Return waiting until showing message has finished + return showMessage.then(function() { + + // Resolve response + resolve(response); + + // Catch errors + }).catch(function() { + + // Resolve response + resolve(response); + }); + } + + // Otherwise + else { + + // Resolve response + resolve(response); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if not canceled + if(canceled === false) { + + // Set prevent cancel + preventCancel = true; + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Clear locked + self.locked = false; + } + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if user rejected the request + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_ACTION_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE)) { + + // Trigger device cancel event + $(self).trigger(HardwareWallet.DEVICE_CANCEL_EVENT); + } + } + } + } + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + } + + // Return sending button acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_BUTTON_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred, true).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if hardware wallet requires passphrase acknowledgment + else if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_PASSPHRASE_REQUEST_MESSAGE_TYPE) { + + // Return sending passphrase acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PASSPHRASE_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Passphrase + "Passphrase": "" + + }, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred, true).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise check if hardware wallet requires pin matrix acknowledgment + else if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_PIN_MATRIX_REQUEST_MESSAGE_TYPE) { + + // Check if not set to fail on lock and text exists + if(failOnLock === false && text !== HardwareWallet.NO_TEXT) { + + // Set locked + self.locked = true; + + // Initialize disconnected + var disconnected = false; + + // Transport on disconnect + var disconnectCallback = self.transport.on("disconnect", function() { + + // Set disconnected + disconnected = true; + + // Clear locked + self.locked = false; + + // Trigger before disconnect event + $(self).trigger(HardwareWallet.BEFORE_DISCONNECT_EVENT); + }); + + // Return showing hardware wallet unlock message + return self.application.showHardwareWalletUnlockMessage(self, text, textArguments, allowUnlock, preventMessages, cancelOccurred).then(function(alphabeticPin) { + + // Check if disconnected + if(disconnected === true) { + + // Check if not preventing unlock message done and not preventing messages + if(preventUnlockMessageDone === false && preventMessages === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + }); + } + + // Otherwise + else { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + } + + // Otherwise + else { + + // Turn off transport on disconnect + self.transport.off("disconnect", disconnectCallback); + + // Clear locked + self.locked = false; + + // Return sending pin matrix acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Pin + "Pin": HardwareWallet.alphabeticPinToPin(Common.removeWhitespace(alphabeticPin)) + + }, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred, true, true).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Resolve response + resolve(response); + }); + } + + // Otherwise + else { + + // Resolve response + resolve(response); + } + } + + // Otherwise + else { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if failure occurred + if(Object.isObject(error) === true && "Message Type" in error === true && error["Message Type"] === HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Initialize error occurred + var errorOccurred = false; + + // Try + try { + + // Decode error + var decodedError = self.decode(error["Message Type"], error["Data"]); + } + + // Catch errors + catch(decodeError) { + + // Set error occurred + errorOccurred = true; + } + + // Check if an error didn't occur + if(errorOccurred === false) { + + // Check if pin was incorrect + if(decodedError["length"] >= Uint8Array["BYTES_PER_ELEMENT"] && (decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE || decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_INVALID_FAILURE_TYPE)) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred, false).then(function() { + + // Check if not preventing messages + if(preventMessages === false) { + + // Message before replace hardware wallet event + $(self.application.message).on(Message.BEFORE_REPLACE_EVENT + ".hardwareWallet", function(event, messageType, messageData) { + + // Check if message type is hardware wallet disconnect message + if(messageType === Application.HARDWARE_WALLET_DISCONNECT_MESSAGE) { + + // Cancel replacing message + self.application.message.cancelReplace(); + + // Return false to stop other replace message + return false; + } + }); + } + + // Return resending message + return self.send(messageType, parameterOne, parameterTwo, data, allowedResponseTypes, (decodedError[0] === HardwareWalletDefinitions.TREZOR_PIN_CANCELED_FAILURE_TYPE) ? Language.getDefaultTranslation('Invalid pin.') : Language.getDefaultTranslation('Incorrect pin.'), [], allowUnlock, failOnLock, true, cancelOccurred, true, true).then(function(response) { + + // Turn off message before replace hardware wallet event + $(self.application.message).off(Message.BEFORE_REPLACE_EVENT + ".hardwareWallet"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Resolve response + resolve(response); + }); + } + + // Otherwise + else { + + // Resolve response + resolve(response); + } + } + + // Otherwise + else { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Catch errors + }).catch(function(error) { + + // Turn off message before replace hardware wallet event + $(self.application.message).off(Message.BEFORE_REPLACE_EVENT + ".hardwareWallet"); + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if not preventing unlock message done and not preventing messages + if(preventUnlockMessageDone === false && preventMessages === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + }); + } + } + } + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Check if not preventing unlock message done + if(preventUnlockMessageDone === false) { + + // Return hiding application hardware unlock message + return self.application.hardwareWalletUnlockMessageDone(preventMessages, cancelOccurred).then(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + }); + } + + // Catch errors + }).catch(function(error) { + + // Check if disconnected + if(disconnected === true) { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + + // Otherwise + else { + + // Turn off transport on disconnect + self.transport.off("disconnect", disconnectCallback); + + // Clear locked + self.locked = false; + + // Return sending pin matrix acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Pin + "Pin": HardwareWallet.INVALID_PIN + + }, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred, true).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function() { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + }); + } + + // Otherwise + else { + + // Return sending pin matrix acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Pin + "Pin": HardwareWallet.INVALID_PIN + + }, allowedResponseTypes, text, textArguments, allowUnlock, failOnLock, preventMessages, cancelOccurred, true).then(function(response) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + } + + // Otherwise check if response type isn't allowed + else if(response["Message Type"] !== ((Array.isArray(allowedResponseTypes) === true) ? allowedResponseTypes[self.transport.type] : allowedResponseTypes)) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if response data isn't used + if(response["Message Type"] !== HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE) { + + // Securely clear response data + response["Data"].fill(0); + } + + // Reject response + reject(response); + } + + // Otherwise + else { + + // Securely clear response data + response["Data"].fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + + // Otherwise + else { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Try + try { + + // Decode response + var decodedResponse = self.decode(response["Message Type"], response["Data"]); + } + + // Catch errors + catch(error) { + + // Securely clear response data + response["Data"].fill(0); + + // Reject error + reject(error); + + // Return + return; + } + + // Securely clear response data + response["Data"].fill(0); + + // Resolve decoded response + resolve(decodedResponse); + } + + // Otherwise + else { + + // Securely clear response data + response["Data"].fill(0); + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + } + } + + // Otherwise + else { + + // Securely clear response data + response["Data"].fill(0); + + // Check if unlock message isn't shown + if(unlockMessageShown === false) { + + // Check if hardware wallet requires button acknowledgment + if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_BUTTON_REQUEST_MESSAGE_TYPE) { + + // Return sending button acknowledge response and catch errors + return self.send(HardwareWalletDefinitions.TREZOR_BUTTON_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_DATA, [], HardwareWallet.NO_TEXT, [], false, true).catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise check if hardware wallet requires passphrase acknowledgment + else if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_PASSPHRASE_REQUEST_MESSAGE_TYPE) { + + // Return sending passphrase acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PASSPHRASE_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Passphrase + "Passphrase": "" + + }, [], HardwareWallet.NO_TEXT, [], false, true).catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + + // Otherwise check if hardware wallet requires pin matrix acknowledgment + else if(self.transport.type === HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE && response["Message Type"] === HardwareWalletDefinitions.TREZOR_PIN_MATRIX_REQUEST_MESSAGE_TYPE) { + + // Return sending pin matrix acknowledge response + return self.send(HardwareWalletDefinitions.TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE, HardwareWallet.NO_PARAMETER, HardwareWallet.NO_PARAMETER, { + + // Pin + "Pin": HardwareWallet.INVALID_PIN + + }, [], HardwareWallet.NO_TEXT, [], false, true).catch(function(error) { + + // Finally + }).finally(function() { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + }); + } + } + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + + // Catch errors + }).catch(function(error) { + + // Check if cancel didn't occur + if(cancelOccurred === Common.NO_CANCEL_OCCURRED || cancelOccurred() === false) { + + // Check if error is that the device was disconnected + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWallet.NETWORK_ERROR_CODE) || ("name" in error === true && error["name"] === "NetworkError"))) { + + // Reject disconnected error + reject(HardwareWallet.DISCONNECTED_ERROR); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Otherwise + else { + + // Reject canceled error + reject(Common.CANCELED_ERROR); + } + }); + } + + // Encode + encode(messageType, data) { + + // Check if data is no data + if(data === HardwareWallet.NO_DATA) { + + // Return nothing + return new Uint8Array([]); + } + + // Check transport's type + switch(this.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Get message schema + var messageSchema = HardwareWalletDefinitions.SCHEMA[messageType.toFixed()]; + + // Initialize result + var result = new Uint8Array([]); + + // Go through all fields in the message schema + for(var fieldNumber in messageSchema) { + + if(messageSchema.hasOwnProperty(fieldNumber) === true) { + + // Check if field isn't ignored + if(HardwareWalletDefinitions.LEDGER_IGNORE_FIELD_NAMES.indexOf(messageSchema[fieldNumber]["Name"]) === Common.INDEX_NOT_FOUND) { + + // Go through all values in the data + for(var name in data) { + + if(data.hasOwnProperty(name) === true) { + + // Check if data is for the field + if(name === messageSchema[fieldNumber]["Name"]) { + + // Check if data is a big number + if(data[name] instanceof BigNumber === true) { + + // Set field payload + var fieldPayload = data[name].toBytes(BigNumber.LITTLE_ENDIAN, messageSchema[fieldNumber]["Size"]); + } + + // Otherwise check if data is bytes + else if(data[name] instanceof Uint8Array === true) { + + // Set field payload + var fieldPayload = data[name]; + } + + // Otherwise check if data is a string + else if(typeof data[name] === "string") { + + // Set field payload + var fieldPayload = (new TextEncoder()).encode(data[name]); + } + + // Otherwise check if data is a number + else if(typeof data[name] === "number") { + + // Set field payload + var fieldPayload = new Uint8Array([data[name]]); + } + + // Append field payload to the result + result = Common.mergeArrays([ + + // Result + result, + + // Field payload + fieldPayload + ]); + + // Break + break; + } + } + } + } + } + } + + // Return result + return result; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Return data encoded as Protocol Buffers + return ProtocolBuffers.encode(messageType, data, HardwareWalletDefinitions.SCHEMA); + } + } + + // Decode + decode(messageType, data, transportType = HardwareWallet.NO_TRANSPORT_TYPE) { + + // Check transport's type + switch((transportType !== HardwareWallet.NO_TRANSPORT_TYPE) ? transportType : this.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Return data + return new Uint8Array(data); + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Decode data as Protocol Buffers + var protocolBuffers = ProtocolBuffers.decode(messageType, data, HardwareWalletDefinitions.SCHEMA); + + // Get message schema + var messageSchema = HardwareWalletDefinitions.SCHEMA[messageType.toFixed()]; + + // Initialize result + var result = new Uint8Array([]); + + // Go through all fields in the message schema + for(var fieldNumber in messageSchema) { + + if(messageSchema.hasOwnProperty(fieldNumber) === true) { + + // Check if field doesn't exist in the Protocol Buffers + if(messageSchema[fieldNumber]["Name"] in protocolBuffers === false) { + + // Check if field is optional + if("Optional" in messageSchema[fieldNumber] === true && messageSchema[fieldNumber]["Optional"] === true) { + + // Go to next field + continue; + } + + // Check if field's type is bool + if(messageSchema[fieldNumber]["Type"] === ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE) { + + // Set field payload to false + protocolBuffers[messageSchema[fieldNumber]["Name"]] = [false]; + } + + // Otherwise + else { + + // Securely clear result + result.fill(0); + + // Throw error + throw "Field doesn't exist in the Protocol Buffers."; + } + } + + // Get field payload + var fieldPayload = protocolBuffers[messageSchema[fieldNumber]["Name"]]; + + // Set include length + var includeLength = false; + + // Check field's expected type + switch(messageSchema[fieldNumber]["Type"]) { + + // Uint + case ProtocolBuffers.UINT_SCHEMA_DATA_TYPE: + + // Set field data + var fieldData = fieldPayload[fieldPayload["length"] - 1].toBytes(BigNumber.BIG_ENDIAN, messageSchema[fieldNumber]["Size"]); + + // Break + break; + + // Bool + case ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE: + + // Set field data + var fieldData = new Uint8Array([(fieldPayload[fieldPayload["length"] - 1] === true) ? 1 : 0]); + + // Break + break; + + // Enum + case ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE: + + // Set field data + var fieldData = new Uint8Array(fieldPayload); + + // Check if more than one value exists in the field payload + if(fieldPayload["length"] > 1) { + + // Check if field length is too big + if(fieldPayload["length"] > Common.BYTE_MAX_VALUE) { + + // Throw error + throw "Field length is too big."; + } + + // Set field data to include field length + fieldData = Common.mergeArrays([new Uint8Array([fieldPayload["length"]]), fieldData]); + } + + // Break + break; + + // String + case ProtocolBuffers.STRING_SCHEMA_DATA_TYPE: + + // Set field data + var fieldData = (new TextEncoder()).encode(fieldPayload[fieldPayload["length"] - 1]); + + // Set include length + includeLength = true; + + // Check if field length is too big + if(fieldData["length"] > Common.BYTE_MAX_VALUE) { + + // Throw error + throw "Field length is too big."; + } + + // Break + break; + + // Bytes + case ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE: + + // Set field data + var fieldData = fieldPayload[fieldPayload["length"] - 1]; + + // Break + break; + + // Sint + case ProtocolBuffers.SINT_SCHEMA_DATA_TYPE: + + // Throw error + throw "Field type not supported."; + } + + // Append field data to the result + var currentResult = new Uint8Array(result["length"] + ((includeLength === true) ? Uint8Array["BYTES_PER_ELEMENT"] : 0) + fieldData["length"]); + currentResult.set(result); + + if(includeLength === true) { + currentResult[result["length"]] = fieldData["length"]; + } + + currentResult.set(fieldData, result["length"] + ((includeLength === true) ? Uint8Array["BYTES_PER_ELEMENT"] : 0)); + result.fill(0); + result = currentResult; + } + } + + // Return result + return result; + } + } + + // Get minimum compatible version + getMinimumCompatibleVersion() { + + // Check transport's type + switch(this.transport.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Return minimum compatible version + return "7.4.1"; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check transport's product name + switch(this.transport["deviceModel"]["productName"]) { + + // Trezor Model One + case "Trezor Model One": + + // Return minimum compatible version + return "1.12.2"; + + // Trezor Model T, Trezor Safe 3, Safe 5, or default + case "Trezor Model T": + case "Trezor Safe 3": + case "Trezor Safe 5": + default: + + // Return minimum compatible version + return "2.6.4"; + } + } + } + + // Alphabetic pin to pin + static alphabeticPinToPin(alphabeticPin) { + + // Check if alphabetic pin is empty + if(alphabeticPin["length"] === 0) { + + // Return invalid pin + return HardwareWallet.INVALID_PIN; + } + + // Initialize result + var result = ""; + + // Go through all characters in the alphabetic pin + for(var i = 0; i < alphabeticPin["length"]; ++i) { + + // Check if character is valid + if(alphabeticPin[i] in HardwareWallet.ALPHABETIC_PIN_CHARACTERS === true) { + + // Append pin character to result + result += HardwareWallet.ALPHABETIC_PIN_CHARACTERS[alphabeticPin[i]]; + } + + // Otherwise + else { + + // Return invalid pin + return HardwareWallet.INVALID_PIN; + } + } + + // Return result + return result; + } + + // Alphabetic pin characters + static get ALPHABETIC_PIN_CHARACTERS() { + + // Return alphabetic pin characters + return { + + // Upper case a + "A": "7", + + // Upper case b + "B": "8", + + // Upper case c + "C": "9", + + // Upper case d + "D": "4", + + // Upper case e + "E": "5", + + // Upper case f + "F": "6", + + // Upper case g + "G": "1", + + // Upper case h + "H": "2", + + // Upper case i + "I": "3", + + // Lower case a + "a": "7", + + // Lower case b + "b": "8", + + // Lower case c + "c": "9", + + // Lower case d + "d": "4", + + // Lower case e + "e": "5", + + // Lower case f + "f": "6", + + // Lower case g + "g": "1", + + // Lower case h + "h": "2", + + // Lower case i + "i": "3" + }; + } + + // Invalid pin + static get INVALID_PIN() { + + // Return invalid pin + return "A"; + } + + // Is compatible version + static isCompatibleVersion(version, minimumCompatibleVersion) { + + // Get version parts from the version + var versionParts = version.match(HardwareWallet.VERSION_STRING_PATTERN); + + // Check if getting version parts was successful + if(versionParts !== Common.NO_MATCH_FOUND) { + + // Get minimum compatible version parts + var minimumCompatibleVersionParts = minimumCompatibleVersion.match(HardwareWallet.VERSION_STRING_PATTERN); + + // Go through all version parts + for(var i = 0; i < versionParts["length"]; ++i) { + + // Check if version part is greater than the minimum compatible version part + if((new BigNumber(versionParts[i])).isGreaterThan(minimumCompatibleVersionParts[i]) === true) { + + // Return true + return true; + } + + // Otherwise check if version part is less than the minimum compatible version part + else if((new BigNumber(versionParts[i])).isLessThan(minimumCompatibleVersionParts[i]) === true) { + + // Return false + return false; + } + } + + // Return true + return true; + } + + // Return false + return false; + } + + // Exclusive lock release event + static get EXCLUSIVE_LOCK_RELEASE_EVENT() { + + // Return exclusive lock release event + return "HardwareWalletExclusiveLockReleaseEvent"; + } + + // No transport + static get NO_TRANSPORT() { + + // Return no transport + return null; + } + + // No root public key + static get NO_ROOT_PUBLIC_KEY() { + + // Return no root public key + return null; + } + + // No seed cookie + static get NO_SEED_COOKIE() { + + // Return no seed cookie + return null; + } + + // No address + static get NO_ADDRESS() { + + // Return no address + return null; + } + + // No kernel commit + static get NO_KERNEL_COMMIT() { + + // Return no kernel commit + return null; + } + + // No payment proof + static get NO_PAYMENT_PROOF() { + + // Return no payment proof + return null; + } + + // Application name + static get APPLICATION_NAME() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network type + case Consensus.MAINNET_NETWORK_TYPE: + + // Return application name + return "MimbleWimble Coin"; + + // Testnet network type + case Consensus.TESTNET_NETWORK_TYPE: + + // Return application name + return "MimbleWimble Coin Floonet"; + } + + // Break + break; + + // GRIN wallet + case Consensus.GRIN_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network type + case Consensus.MAINNET_NETWORK_TYPE: + + // Return application name + return "Grin"; + + // Testnet network type + case Consensus.TESTNET_NETWORK_TYPE: + + // Return application name + return "Grin Testnet"; + } + + // Break + break; + + // EPIC wallet + case Consensus.EPIC_WALLET_TYPE: + + // Check network type + switch(Consensus.getNetworkType()) { + + // Mainnet network type + case Consensus.MAINNET_NETWORK_TYPE: + + // Return application name + return "Epic Cash"; + + // Testnet network type + case Consensus.TESTNET_NETWORK_TYPE: + + // Return application name + return "Epic Cash Floonet"; + } + + // Break + break; + } + } + + // MimbleWimble Coin capable + static get MIMBLEWIMBLE_COIN_CAPABLE() { + + // Return MimbleWimble Coin capable + return 0xC7; + } + + // Version string pattern + static get VERSION_STRING_PATTERN() { + + // Return hex string pattern + return /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u; + } + + // Seed cookie length + static get SEED_COOKIE_LENGTH() { + + // Return seed cookie length + return 64; + } + + // No parameter + static get NO_PARAMETER() { + + // Return no parameter + return 0; + } + + // Account + static get ACCOUNT() { + + // Return account + return 0; + } + + // MQS address type + static get MQS_ADDRESS_TYPE() { + + // Return MQS address type + return 0; + } + + // Tor address type + static get TOR_ADDRESS_TYPE() { + + // Return Tor address type + return HardwareWallet.MQS_ADDRESS_TYPE + 1; + } + + // Slatepack address type + static get SLATEPACK_ADDRESS_TYPE() { + + // Return Slatepack address type + return HardwareWallet.TOR_ADDRESS_TYPE + 1; + } + + // No data + static get NO_DATA() { + + // Return no data + return null; + } + + // Resend request delay milliseconds + static get RESEND_REQUEST_DELAY_MILLISECONDS() { + + // Return resend request delay milliseconds + return 50; + } + + // Not found error code + static get NOT_FOUND_ERROR_CODE() { + + // Return not found error code + return 8; + } + + // Invalid state error code + static get INVALID_STATE_ERROR_CODE() { + + // Return invalid state error code + return 11; + } + + // Network error code + static get NETWORK_ERROR_CODE() { + + // Return network error code + return 19; + } + + // No salt + static get NO_SALT() { + + // Return no salt + return null; + } + + // Encryption and decryption maximum chunk size + static get ENCRYPTION_AND_DECRYPTION_MAXIMUM_CHUNK_SIZE() { + + // Return encryption and decryption maximum chunk size + return 64; + } + + // No transport type + static get NO_TRANSPORT_TYPE() { + + // Return no transport type + return null; + } +} + + +// Main function + +// Set global object's hardware wallet +globalThis["HardwareWallet"] = HardwareWallet; diff --git a/scripts/hardware_wallet_bluetooth_transport.js b/scripts/hardware_wallet_bluetooth_transport.js new file mode 100755 index 0000000..6422638 --- /dev/null +++ b/scripts/hardware_wallet_bluetooth_transport.js @@ -0,0 +1,1277 @@ +// Use strict +"use strict"; + + +// Classes + +// HardwareWallet Bluetooth transport class +class HardwareWalletBluetoothTransport { + + // Public + + // Constructor + constructor(connection, writeCharacteristic, notifyCharacteristic, mtu, productName) { + + // Set connection + this.connection = connection; + + // Set write characteristic + this.writeCharacteristic = writeCharacteristic; + + // Set notify characteristic + this.notifyCharacteristic = notifyCharacteristic; + + // Set MTU + this.mtu = mtu; + + // Set allow disconnect event to true + this.allowDisconnectEvent = true; + + // Set type to Ledger + this.type = HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE; + + // Set device model + this["deviceModel"] = { + + // Product name + "productName": productName + }; + } + + // On + on(event, callback) { + + // Check event + switch(event) { + + // Disconnect + case "disconnect": + + // Set self + var self = this; + + // Create callback once + var callbackOnce = function() { + + // Remove GATT server disconnected event + self.connection["device"].removeEventListener("gattserverdisconnected", callbackOnce); + + // Check if disconnect event is allowed + if(self.allowDisconnectEvent === true) { + + // Call callback + callback(); + } + }; + + // Device GATT server disconnected event + this.connection["device"].addEventListener("gattserverdisconnected", callbackOnce); + + // Return callback once + return callbackOnce; + } + } + + // Off + off(event, callback) { + + // Check event + switch(event) { + + // Disconnect + case "disconnect": + + // Remove GATT server disconnected event + this.connection["device"].removeEventListener("gattserverdisconnected", callback); + + // Break + break; + } + } + + // Close + close() { + + // Clear allow disconnect event + this.allowDisconnectEvent = false; + + // Check if connection is connected + if(this.connection["connected"] === true) { + + // Disconnect connection + this.connection.disconnect(); + } + + // Return promise + return new Promise(function(resolve, reject) { + + // Resolve + resolve(); + }); + } + + // Send + send(messageType, parameterOne, parameterTwo, data) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if connection is connected + if(self.connection["connected"] === true) { + + // Create header + var header = new Uint8Array([messageType >>> HardwareWalletBluetoothTransport.BITS_IN_A_BYTE, messageType, parameterOne, parameterTwo, data["length"]]); + + // Create payload + var payload = new Uint8Array(header["length"] + data["length"]); + payload.set(header); + payload.set(data, header["length"]); + + // Return sending request to the device + return HardwareWalletBluetoothTransport.sendRequest(self.connection, self.writeCharacteristic, self.notifyCharacteristic, HardwareWalletBluetoothTransport.LEDGER_SEND_REQUEST_COMMAND_TAG, payload, self.mtu).then(function(response) { + + // Check if connection is connected + if(self.connection["connected"] === true) { + + // Check if response contains a message type + if(response["length"] >= HardwareWalletBluetoothTransport.MESSAGE_TYPE_LENGTH) { + + // Get message type + var messageType = (response[response["length"] - HardwareWalletBluetoothTransport.MESSAGE_TYPE_LENGTH] << HardwareWalletBluetoothTransport.BITS_IN_A_BYTE) | response[response["length"] - (HardwareWalletBluetoothTransport.MESSAGE_TYPE_LENGTH - 1)]; + + // Resolve + resolve({ + + // Message type + "Message Type": messageType, + + // Data + "Data": response.subarray(0, response["length"] - HardwareWalletBluetoothTransport.MESSAGE_TYPE_LENGTH) + }); + } + + // Otherwise + else { + + // Securely clear response + response.fill(0); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Securely clear response + response.fill(0); + + // Reject error + reject(new DOMException("", "NetworkError")); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(self.connection["connected"] === true) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + } + + // Request + static request(device = HardwareWalletBluetoothTransport.NO_DEVICE) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Get device + var getDevice = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if no device was provided + if(device === HardwareWalletBluetoothTransport.NO_DEVICE) { + + // Return getting device + return navigator["bluetooth"].requestDevice({ + + // Filters + "filters": HardwareWalletBluetoothTransport.DEVICES.map(function(device) { + + // Return device's service UUID + return { + + // Services + "services": [device["Service UUID"]] + }; + }) + }).then(function(device) { + + // Check if device isn't connected + if(device["gatt"]["connected"] === false) { + + // Resolve device + resolve(device); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "InvalidStateError")); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve device + resolve(device); + } + }); + }; + + // Return getting device + return getDevice().then(function(device) { + + // Get connection + var getConnection = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if device's connection is already connected + if(device["gatt"]["connected"] === true) { + + // Resolve connection + resolve(device["gatt"]); + } + + // Otherwise + else { + + // Return getting connection to the device + return device["gatt"].connect().then(function(connection) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Resolve connection + resolve(connection); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + }); + }; + + // Return getting connection + return getConnection().then(function(connection) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Initialize timeout occurred + var timeoutOccurred = false; + + // Connection timeout + var connectTimeout = setTimeout(function() { + + // Set timeout occurred + timeoutOccurred = true; + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject + reject(); + + }, HardwareWalletBluetoothTransport.CONNECT_TIMEOUT_DURATION_MILLISECONDS); + + // Return getting connection's services + return connection.getPrimaryServices().then(function(services) { + + // Check if a timeout didn't occur + if(timeoutOccurred === false) { + + // Clear connect timeout + clearTimeout(connectTimeout); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Initialize device found + var deviceFound = false; + + // Go through all services + for(var i = 0; i < services["length"] && deviceFound === false; ++i) { + + // Check if service has a UUID + if("uuid" in services[i] === true) { + + // Go through all devices + for(var j = 0; j < HardwareWalletBluetoothTransport.DEVICES["length"]; ++j) { + + // Check if device's service UUID is the service's UUID + if(HardwareWalletBluetoothTransport.DEVICES[j]["Service UUID"] === services[i]["uuid"]) { + + // Set device found + deviceFound = true; + + // Set device index + var deviceIndex = j; + + // Set service + var service = services[i]; + + // Break + break; + } + } + } + } + + // Check if device was found + if(deviceFound === true) { + + // Return getting service's notify characteristic + return service.getCharacteristic(HardwareWalletBluetoothTransport.DEVICES[deviceIndex]["Notify Characteristic UUID"]).then(function(notifyCharacteristic) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Return getting service's write characteristic + return service.getCharacteristic(HardwareWalletBluetoothTransport.DEVICES[deviceIndex]["Write Characteristic UUID"]).then(function(writeCharacteristic) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Return starting notify characteristic's notifications + return notifyCharacteristic.startNotifications().then(function() { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnected handler + var disconnectedHandler = function() { + + // Remove GATT server disconnected event + device.removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Stop notifications and catch errors + notifyCharacteristic.stopNotifications().catch(function(error) { + + }); + }; + + // Device GATT server disconnected event + device.addEventListener("gattserverdisconnected", disconnectedHandler); + + // Return getting MTU from the device + return HardwareWalletBluetoothTransport.sendRequest(connection, writeCharacteristic, notifyCharacteristic, HardwareWalletBluetoothTransport.LEDGER_GET_MTU_COMMAND_TAG).then(function(response) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Check if response is valid + if(response["length"] === 1) { + + // Get MTU from response + var mtu = Math.min(response[0], HardwareWalletBluetoothTransport.MAXIMUM_MTU); + + // Check if MTU is valid + if(mtu >= HardwareWalletBluetoothTransport.MINIMUM_MTU) { + + // Create transport + var transport = new HardwareWalletBluetoothTransport(connection, writeCharacteristic, notifyCharacteristic, mtu, HardwareWalletBluetoothTransport.DEVICES[deviceIndex]["Product Name"]); + + // Resolve transport + resolve(transport); + } + + // Otherwise + else { + + // Disconnect connection + connection.disconnect(); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Disconnect connection + connection.disconnect(); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Return stopping notifications and catch errors + return notifyCharacteristic.stopNotifications().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject + reject(); + }); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + } + + // Catch errors + }).catch(function(error) { + + // Check if a timeout didn't occur + if(timeoutOccurred === false) { + + // Clear connect timeout + clearTimeout(connectTimeout); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Disconnect connection + connection.disconnect(); + } + + // Check if disconnected error occurred + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWalletBluetoothTransport.NETWORK_ERROR_CODE) || ("name" in error === true && error["name"] === "NetworkError"))) { + + // Return requesting transport + return HardwareWalletBluetoothTransport.request(device).then(function(transport) { + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + } + }); + } + + // Otherwise + else { + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check if device's connection is connected + if(device["gatt"]["connected"] === true) { + + // Disconnect device's connection + device["gatt"].disconnect(); + } + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Private + + // Create packets + static createPackets(commandTag, payload = HardwareWalletBluetoothTransport.NO_PAYLOAD, mtu = HardwareWalletBluetoothTransport.DEFAULT_MTU) { + + // Initialize packets + var packets = []; + + // Check if payload doesn't exist + if(payload === HardwareWalletBluetoothTransport.NO_PAYLOAD) { + + // Set payload to an empty array + payload = new Uint8Array([]); + } + + // Initialize payload offset + var payloadOffset = 0; + + // Go through all packets required to send the payload + for(var i = 0; i === 0 || payloadOffset !== payload["length"]; ++i) { + + // Check if at the first packet + if(i === 0) { + + // Create header + var header = new Uint8Array([commandTag, i >>> HardwareWalletBluetoothTransport.BITS_IN_A_BYTE, i, payload["length"] >>> HardwareWalletBluetoothTransport.BITS_IN_A_BYTE, payload["length"]]); + } + + // Otherwise + else { + + // Create header + var header = new Uint8Array([commandTag, i >>> HardwareWalletBluetoothTransport.BITS_IN_A_BYTE, i]); + } + + // Get payload part length + var payloadPartLength = Math.min(payload["length"] - payloadOffset, mtu - header["length"]); + + // Create packet + var packet = new Uint8Array(header["length"] + payloadPartLength); + packet.set(header); + packet.set(payload.subarray(payloadOffset, payloadOffset + payloadPartLength), header["length"]); + + // Append packet to list + packets.push(packet); + + // Update payload offset + payloadOffset += payloadPartLength; + } + + // Return packets + return packets; + } + + // Send request + static sendRequest(connection, writeCharacteristic, notifyCharacteristic, commandTag, payload = HardwareWalletBluetoothTransport.NO_PAYLOAD, mtu = HardwareWalletBluetoothTransport.DEFAULT_MTU) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Initialize response + var response = new Uint8Array([]); + + // Initialize response size + var responseSize; + + // Initialize first response packet + var firstResponsePacket = true; + + // Initialize sequence index + var lastSequenceIndex; + + // Process response packet + var processResponsePacket = function(event) { + + // Get response packet + var responsePacket = new Uint8Array(event["target"]["value"]["buffer"]); + + // Check if response packet is too short + if((firstResponsePacket === true && responsePacket["length"] < HardwareWalletBluetoothTransport.LEDGER_FIRST_PACKET_HEADER_LENGTH) || (firstResponsePacket === false && responsePacket["length"] <= HardwareWalletBluetoothTransport.LEDGER_NEXT_PACKETS_HEADER_LENGTH)) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response packet + responsePacket.fill(0); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + } + + // Otherwise + else { + + // Get tag + var tag = responsePacket[0]; + + // Check if tag is invalid + if(tag !== commandTag) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response packet + responsePacket.fill(0); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + } + + // Otherwise + else { + + // Get sequence index + var sequenceIndex = (responsePacket[Uint8Array["BYTES_PER_ELEMENT"]] << HardwareWalletBluetoothTransport.BITS_IN_A_BYTE) | responsePacket[Uint8Array["BYTES_PER_ELEMENT"] + 1]; + + // Check if first response packet + if(firstResponsePacket === true) { + + // Check if sequence index is invalid + if(sequenceIndex !== 0) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response packet + responsePacket.fill(0); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + + // Return + return; + } + + // Clear first response packet + firstResponsePacket = false; + + // Get response size + responseSize = (responsePacket[Uint8Array["BYTES_PER_ELEMENT"] + Uint16Array["BYTES_PER_ELEMENT"]] << HardwareWalletBluetoothTransport.BITS_IN_A_BYTE) | responsePacket[Uint8Array["BYTES_PER_ELEMENT"] + Uint16Array["BYTES_PER_ELEMENT"] + 1]; + + // Get response part + var responsePart = responsePacket.subarray(HardwareWalletBluetoothTransport.LEDGER_FIRST_PACKET_HEADER_LENGTH); + } + + // Otherwise + else { + + // Check if sequence index is invalid + if(sequenceIndex !== lastSequenceIndex + 1) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response packet + responsePacket.fill(0); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + + // Return + return; + } + + // Get response part + var responsePart = responsePacket.subarray(HardwareWalletBluetoothTransport.LEDGER_NEXT_PACKETS_HEADER_LENGTH); + } + + // Update last sequence index + lastSequenceIndex = sequenceIndex; + + // Append response part to response + var currentResponse = new Uint8Array(response["length"] + responsePart["length"]); + currentResponse.set(response); + currentResponse.set(responsePart, response["length"]); + response.fill(0); + responsePart.fill(0); + response = currentResponse; + + // Check if response is too large + if(response["length"] > responseSize) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject + reject(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + } + + // Otherwise check if entire response has been received + else if(response["length"] === responseSize) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Resolve response + resolve(response); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + } + } + } + }; + + // Disconnected handler + var disconnectedHandler = function() { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response + response.fill(0); + + // Reject error + reject(new DOMException("", "NetworkError")); + }; + + // Notify characteristic value changed event + notifyCharacteristic.addEventListener("characteristicvaluechanged", processResponsePacket); + + // Device GATT server disconnected event + connection["device"].addEventListener("gattserverdisconnected", disconnectedHandler); + + // Get packets + var packets = HardwareWalletBluetoothTransport.createPackets(commandTag, payload, mtu); + + // Send packet + var sendPacket = new Promise(function(resolve, reject) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + + // Initialize sending packets + var sendingPackets = [sendPacket]; + + // Go through all packets + for(var i = 0; i < packets["length"]; ++i) { + + // Get packet + let packet = packets[i]; + + // Send next pack after previous packet is send + sendPacket = sendPacket.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Return writing packet + return writeCharacteristic.writeValueWithResponse(packet).then(function() { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Resolve + resolve(); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + + // Catch errors + }).catch(function(error) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + }); + + // Append sending packet to list + sendingPackets.push(sendPacket); + } + + // Return sending all packets and catch errors + return Promise.all(sendingPackets).catch(function(error) { + + // Remove GATT server disconnected event + connection["device"].removeEventListener("gattserverdisconnected", disconnectedHandler); + + // Remove notify characteristic value changed event + notifyCharacteristic.removeEventListener("characteristicvaluechanged", processResponsePacket); + + // Securely clear response + response.fill(0); + + // Check if connection is connected + if(connection["connected"] === true) { + + // Reject error + reject(error); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + }); + } + + // Devices + static get DEVICES() { + + // Return devices + return [ + + // Ledger Nano X + { + + // Product name + "Product Name": "Ledger Nano X", + + // Service UUID + "Service UUID": "13d63400-2c97-0004-0000-4c6564676572", + + // Notify characteristic UUID + "Notify Characteristic UUID": "13d63400-2c97-0004-0001-4c6564676572", + + // Write characteristic UUID + "Write Characteristic UUID": "13d63400-2c97-0004-0002-4c6564676572" + }, + + // Ledger Stax + { + + // Product name + "Product Name": "Ledger Stax", + + // Service UUID + "Service UUID": "13d63400-2c97-6004-0000-4c6564676572", + + // Notify characteristic UUID + "Notify Characteristic UUID": "13d63400-2c97-6004-0001-4c6564676572", + + // Write characteristic UUID + "Write Characteristic UUID": "13d63400-2c97-6004-0002-4c6564676572" + }, + + // Ledger Flex + { + + // Product name + "Product Name": "Ledger Flex", + + // Service UUID + "Service UUID": "13d63400-2c97-3004-0000-4c6564676572", + + // Notify characteristic UUID + "Notify Characteristic UUID": "13d63400-2c97-3004-0001-4c6564676572", + + // Write characteristic UUID + "Write Characteristic UUID": "13d63400-2c97-3004-0002-4c6564676572" + } + ]; + } + + // Default MTU + static get DEFAULT_MTU() { + + // Return default MTU + return 20; + } + + // Minimum MTU + static get MINIMUM_MTU() { + + // Return minimum MTU + return 6; + } + + // Maximum MTU + static get MAXIMUM_MTU() { + + // Return maximum MTU + return 100; + } + + // No payload + static get NO_PAYLOAD() { + + // Return no payload + return null; + } + + // Ledger get MTU command tag + static get LEDGER_GET_MTU_COMMAND_TAG() { + + // Return Ledger get MTU command tag + return 0x08; + } + + // Ledger send request command tag + static get LEDGER_SEND_REQUEST_COMMAND_TAG() { + + // Return Ledger send request command tag + return 0x05; + } + + // Message type length + static get MESSAGE_TYPE_LENGTH() { + + // Return message type length + return Uint16Array["BYTES_PER_ELEMENT"]; + } + + // No device + static get NO_DEVICE() { + + // Return no device + return null; + } + + // Connect timeout duration milliseconds + static get CONNECT_TIMEOUT_DURATION_MILLISECONDS() { + + // Return connect timeout duration milliseconds + return 4000; + } + + // Bits in a byte + static get BITS_IN_A_BYTE() { + + // Rerurn bits in a byte + return 8; + } + + // Ledger first packet header length + static get LEDGER_FIRST_PACKET_HEADER_LENGTH() { + + // Return Ledger first packet header length + return 5; + } + + // Ledger next packets header length + static get LEDGER_NEXT_PACKETS_HEADER_LENGTH() { + + // Return Ledger next packets header length + return 3; + } + + // Network error code + static get NETWORK_ERROR_CODE() { + + // Return network error code + return 19; + } +} + + +// Main function + +// Set global object's hardware wallet Bluetooth transport +globalThis["HardwareWalletBluetoothTransport"] = HardwareWalletBluetoothTransport; diff --git a/scripts/hardware_wallet_definitions.js b/scripts/hardware_wallet_definitions.js new file mode 100755 index 0000000..0c760eb --- /dev/null +++ b/scripts/hardware_wallet_definitions.js @@ -0,0 +1,2066 @@ +// Use strict +"use strict"; + + +// Classes + +// Hardware wallet definitions class +class HardwareWalletDefinitions { + + // Public + + // Ledger transport type + static get LEDGER_TRANSPORT_TYPE() { + + // Return Ledger transport type + return 0; + } + + // Trezor transport type + static get TREZOR_TRANSPORT_TYPE() { + + // Return Trezor type + return HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE + 1; + } + + // Ledger get application information message type + static get LEDGER_GET_APPLICATION_INFORMATION_MESSAGE_TYPE() { + + // Return Ledger get application information message type + return 0xB001; + } + + // Ledger device locked message type + static get LEDGER_DEVICE_LOCKED_MESSAGE_TYPE() { + + // Return Ledger device locked message type + return 0x5515; + } + + // Ledger success message type + static get LEDGER_SUCCESS_MESSAGE_TYPE() { + + // Return Ledger success message type + return 0x9000; + } + + // Ledger user rejected message type + static get LEDGER_USER_REJECTED_MESSAGE_TYPE() { + + // Return Ledger user rejected message type + return 0xB103; + } + + // Ledger app locked message type + static get LEDGER_APP_LOCKED_MESSAGE_TYPE() { + + // Return Ledger app locked message type + return 0xD102; + } + + // Trezor initialize message type + static get TREZOR_INITIALIZE_MESSAGE_TYPE() { + + // Return Trezor initialize message type + return 0x0000; + } + + // Trezor success message type + static get TREZOR_SUCCESS_MESSAGE_TYPE() { + + // Return Trezor success message type + return 0x0002; + } + + // Trezor failure message type + static get TREZOR_FAILURE_MESSAGE_TYPE() { + + // Return Trezor failure message type + return 0x0003; + } + + // Trezor Load device message type + static get TREZOR_LOAD_DEVICE_MESSAGE_TYPE() { + + // Return Trezor load device message type + return 0x000D; + } + + // Trezor features message type + static get TREZOR_FEATURES_MESSAGE_TYPE() { + + // Return Trezor features message type + return 0x0011; + } + + // Trezor pin matrix request message type + static get TREZOR_PIN_MATRIX_REQUEST_MESSAGE_TYPE() { + + // Return Trezor pin matrix request message type + return 0x0012; + } + + // Trezor pin matrix acknowledge message type + static get TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE() { + + // Return Trezor pin matrix acknowledge message type + return 0x0013; + } + + // Trezor lock device message type + static get TREZOR_LOCK_DEVICE_MESSAGE_TYPE() { + + // Return Trezor lock device message type + return 0x0018; + } + + // Trezor apply settings message type + static get TREZOR_APPLY_SETTINGS_MESSAGE_TYPE() { + + // Return Trezor apply settings message type + return 0x0019; + } + + // Trezor button request message type + static get TREZOR_BUTTON_REQUEST_MESSAGE_TYPE() { + + // Return Trezor button request message type + return 0x001A; + } + + // Trezor button acknowledge message type + static get TREZOR_BUTTON_ACKNOWLEDGE_MESSAGE_TYPE() { + + // Return Trezor button acknowledge message type + return 0x001B; + } + + // Trezor passphrase request message type + static get TREZOR_PASSPHRASE_REQUEST_MESSAGE_TYPE() { + + // Return Trezor passphrase request message type + return 0x0029; + } + + // Trezor passphrase acknowledge message type + static get TREZOR_PASSPHRASE_ACKNOWLEDGE_MESSAGE_TYPE() { + + // Return Trezor passphrase acknowledge message type + return 0x002A; + } + + // Trezor action canceled failure type + static get TREZOR_ACTION_CANCELED_FAILURE_TYPE() { + + // Return Trezor action canceled failure type + return 0x04; + } + + // Trezor pin canceled failure type + static get TREZOR_PIN_CANCELED_FAILURE_TYPE() { + + // Return Trezor pin canceled failure type + return 0x06; + } + + // Trezor pin invalid failure type + static get TREZOR_PIN_INVALID_FAILURE_TYPE() { + + // Return Trezor pin invalid failure type + return 0x07; + } + + // Trezor passphrase entry button request type + static get TREZOR_PASSPHRASE_ENTRY_BUTTON_REQUEST_TYPE() { + + // Return Trezor passphrase entry button request type + return 0x13; + } + + // Trezor pin entry button request type + static get TREZOR_PIN_ENTRY_BUTTON_REQUEST_TYPE() { + + // Return Trezor pin entry button request type + return 0x14; + } + + // MimbleWimble Coin get root public key message type + static get MIMBLEWIMBLE_COIN_GET_ROOT_PUBLIC_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get root public key message type + return 0xC700; + } + + // MimbleWimble Coin root public key message type + static get MIMBLEWIMBLE_COIN_ROOT_PUBLIC_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin root public key message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ROOT_PUBLIC_KEY_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get address message type + static get MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get address message type + return 0xC701; + } + + // MimbleWimble Coin address message type + static get MIMBLEWIMBLE_COIN_ADDRESS_MESSAGE_TYPE() { + + // Return MimbleWimble Coin address message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get seed cookie message type + static get MIMBLEWIMBLE_COIN_GET_SEED_COOKIE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get seed cookie message type + return 0xC702; + } + + // MimbleWimble Coin seed cookie message type + static get MIMBLEWIMBLE_COIN_SEED_COOKIE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin seed cookie message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_SEED_COOKIE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get commitment message type + static get MIMBLEWIMBLE_COIN_GET_COMMITMENT_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get commitment message type + return 0xC703; + } + + // MimbleWimble Coin commitment message type + static get MIMBLEWIMBLE_COIN_COMMITMENT_MESSAGE_TYPE() { + + // Return MimbleWimble Coin commitment message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_COMMITMENT_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get bulletproof components message type + static get MIMBLEWIMBLE_COIN_GET_BULLETPROOF_COMPONENTS_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get bulletproof components message type + return 0xC704; + } + + // MimbleWimble Coin bulletproof components message type + static get MIMBLEWIMBLE_COIN_BULLETPROOF_COMPONENTS_MESSAGE_TYPE() { + + // Return MimbleWimble Coin bulletproof components message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_BULLETPROOF_COMPONENTS_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin verify root public key message type + static get MIMBLEWIMBLE_COIN_VERIFY_ROOT_PUBLIC_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin verify root public key message type + return 0xC705; + } + + // MimbleWimble Coin verify address message type + static get MIMBLEWIMBLE_COIN_VERIFY_ADDRESS_MESSAGE_TYPE() { + + // Return MimbleWimble Coin verify address message type + return 0xC706; + } + + // MimbleWimble Coin start encrypting slate message type + static get MIMBLEWIMBLE_COIN_START_ENCRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin start encrypting slate message type + return 0xC707; + } + + // MimbleWimble Coin encrypted slate nonce message type + static get MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_NONCE_AND_SALT_MESSAGE_TYPE() { + + // Return MimbleWimble Coin encrypted slate nonce and salt message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_ENCRYPTING_SLATE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin continue encrypting slate message type + static get MIMBLEWIMBLE_COIN_CONTINUE_ENCRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue encrypting slate message type + return 0xC708; + } + + // MimbleWimble Coin encrypted slate data message type + static get MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_DATA_MESSAGE_TYPE() { + + // Return MimbleWimble Coin encrypted slate data message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_ENCRYPTING_SLATE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin finish encrypting slate message type + static get MIMBLEWIMBLE_COIN_FINISH_ENCRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin finish encrypting slate message type + return 0xC709; + } + + // MimbleWimble Coin encrypted slate tag and signature message type + static get MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_TAG_AND_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin encrypted slate tag and signature message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_ENCRYPTING_SLATE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin start decrypting slate message type + static get MIMBLEWIMBLE_COIN_START_DECRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin start decrypting slate message type + return 0xC70A; + } + + // MimbleWimble Coin continue decrypting slate message type + static get MIMBLEWIMBLE_COIN_CONTINUE_DECRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue decrypting slate message type + return 0xC70B; + } + + // MimbleWimble Coin decrypted slate data message type + static get MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_DATA_MESSAGE_TYPE() { + + // Return MimbleWimble Coin decrypted slate data message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_DECRYPTING_SLATE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin finish decrypting slate message type + static get MIMBLEWIMBLE_COIN_FINISH_DECRYPTING_SLATE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin finish decrypting slate message type + return 0xC70C; + } + + // MimbleWimble Coin decrypted slate AES key message type + static get MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_AES_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin decrypted slate AES key message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_DECRYPTING_SLATE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin start transaction message type + static get MIMBLEWIMBLE_COIN_START_TRANSACTION_MESSAGE_TYPE() { + + // Return MimbleWimble Coin start transaction message type + return 0xC70D; + } + + // MimbleWimble Coin continue transaction include output message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_OUTPUT_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction include output message type + return 0xC70E; + } + + // MimbleWimble Coin continue transaction include input message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_INPUT_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction include input message type + return 0xC70F; + } + + // MimbleWimble Coin continue transaction apply offset message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_APPLY_OFFSET_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction apply offset message type + return 0xC710; + } + + // MimbleWimble Coin transaction secret nonce index message type + static get MIMBLEWIMBLE_COIN_TRANSACTION_SECRET_NONCE_INDEX_MESSAGE_TYPE() { + + // Return MimbleWimble Coin transaction secret nonce index message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_APPLY_OFFSET_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin continue transaction get public key message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction get public key message type + return 0xC711; + } + + // MimbleWimble Coin transaction public key message type + static get MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_KEY_MESSAGE_TYPE() { + + // Return MimbleWimble Coin transaction public key message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_KEY_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin continue transaction get public nonce message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_NONCE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction get public nonce message type + return 0xC712; + } + + // MimbleWimble Coin transaction public nonce message type + static get MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_NONCE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin transaction public nonce message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_NONCE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin continue transaction get message signature message type + static get MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_MESSAGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin continue transaction get message signature message type + return 0xC713; + } + + // MimbleWimble Coin transaction message signature message type + static get MIMBLEWIMBLE_COIN_TRANSACTION_MESSAGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin transaction message signature message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_MESSAGE_SIGNATURE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin finish transaction message type + static get MIMBLEWIMBLE_COIN_FINISH_TRANSACTION_MESSAGE_TYPE() { + + // Return MimbleWimble Coin finish transaction message type + return 0xC714; + } + + // MimbleWimble Coin transaction signature and payment proof message type + static get MIMBLEWIMBLE_COIN_TRANSACTION_SIGNATURE_AND_PAYMENT_PROOF_MESSAGE_TYPE() { + + // Return MimbleWimble Coin transaction signature and payment proof message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_TRANSACTION_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get MQS challenge signature message type + static get MIMBLEWIMBLE_COIN_GET_MQS_CHALLENGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get MQS challenge signature message type + return 0xC715; + } + + // MimbleWimble Coin MQS challenge signature message type + static get MIMBLEWIMBLE_COIN_MQS_CHALLENGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin MQS challenge signature message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_MQS_CHALLENGE_SIGNATURE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // MimbleWimble Coin get login challenge signature message type + static get MIMBLEWIMBLE_COIN_GET_LOGIN_CHALLENGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin get login challenge signature message type + return 0xC716; + } + + // MimbleWimble Coin login challenge signature message type + static get MIMBLEWIMBLE_COIN_LOGIN_CHALLENGE_SIGNATURE_MESSAGE_TYPE() { + + // Return MimbleWimble Coin login challenge signature message type + return HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_LOGIN_CHALLENGE_SIGNATURE_MESSAGE_TYPE | HardwareWalletDefinitions.MESSAGE_RESPONSE_MASK; + } + + // Schema + static get SCHEMA() { + + // Return schema + return { + + // Trezor success + [HardwareWalletDefinitions.TREZOR_SUCCESS_MESSAGE_TYPE.toFixed()]: {}, + + // Trezor failure + [HardwareWalletDefinitions.TREZOR_FAILURE_MESSAGE_TYPE.toFixed()]: { + + // Failure type + "1": { + + // Name + "Name": "Failure Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // Trezor load device + [HardwareWalletDefinitions.TREZOR_LOAD_DEVICE_MESSAGE_TYPE.toFixed()]: { + + // Mnemonic + "1": { + + // Name + "Name": "Mnemonic", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + }, + + // Pin + "3": { + + // Name + "Name": "Pin", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + } + }, + + // Trezor features + [HardwareWalletDefinitions.TREZOR_FEATURES_MESSAGE_TYPE.toFixed()]: { + + // Major version + "2": { + + // Name + "Name": "Major Version", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Minor version + "3": { + + // Name + "Name": "Minor Version", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Patch version + "4": { + + // Name + "Name": "Patch Version", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Pin protection + "7": { + + // Name + "Name": "Pin Protection", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + }, + + // Passphrase protection + "8": { + + // Name + "Name": "Passphrase Protection", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + }, + + // Initialized + "12": { + + // Name + "Name": "Initialized", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + }, + + // Unlocked + "16": { + + // Name + "Name": "Unlocked", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + }, + + // Model + "21": { + + // Name + "Name": "Model", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + }, + + // Capabilities + "30": { + + // Name + "Name": "Capabilities", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Passphrase always on device + "36": { + + // Name + "Name": "Passphrase Always On Device", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + } + }, + + // Trezor pin matrix acknowledge + [HardwareWalletDefinitions.TREZOR_PIN_MATRIX_ACKNOWLEDGE_MESSAGE_TYPE.toFixed()]: { + + // Pin + "1": { + + // Name + "Name": "Pin", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + } + }, + + // Trezor apply settings request + [HardwareWalletDefinitions.TREZOR_APPLY_SETTINGS_MESSAGE_TYPE.toFixed()]: { + + // Use passphrase + "3": { + + // Name + "Name": "Use Passphrase", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + }, + + // Passphrase always on device + "8": { + + // Name + "Name": "Passphrase Always On Device", + + // Type + "Type": ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + } + }, + + // Trezor button request + [HardwareWalletDefinitions.TREZOR_BUTTON_REQUEST_MESSAGE_TYPE.toFixed()]: { + + // Button request type + "1": { + + // Name + "Name": "Button Request Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // Trezor passphrase acknowledge + [HardwareWalletDefinitions.TREZOR_PASSPHRASE_ACKNOWLEDGE_MESSAGE_TYPE.toFixed()]: { + + // Passphrase + "1": { + + // Name + "Name": "Passphrase", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get root public key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ROOT_PUBLIC_KEY_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + } + }, + + // MimbleWimble Coin root public key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ROOT_PUBLIC_KEY_MESSAGE_TYPE.toFixed()]: { + + // Root public key + "1": { + + // Name + "Name": "Root Public Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get address + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_ADDRESS_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Address type + "3": { + + // Name + "Name": "Parameter One", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "4": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "5": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + } + }, + + // MimbleWimble Coin address + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ADDRESS_MESSAGE_TYPE.toFixed()]: { + + // Address + "1": { + + // Name + "Name": "Address", + + // Type + "Type": ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get seed cookie + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_SEED_COOKIE_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + } + }, + + // MimbleWimble Coin seed cookie + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_SEED_COOKIE_MESSAGE_TYPE.toFixed()]: { + + // Seed cookie + "1": { + + // Name + "Name": "Seed Cookie", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get commitment + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_COMMITMENT_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Identifier + "4": { + + // Name + "Name": "Identifier", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Value + "5": { + + // Name + "Name": "Value", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Switch type + "6": { + + // Name + "Name": "Switch Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin commitment + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_COMMITMENT_MESSAGE_TYPE.toFixed()]: { + + // Commitment + "1": { + + // Name + "Name": "Commitment", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get bulletproof components + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_BULLETPROOF_COMPONENTS_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Message type + "3": { + + // Name + "Name": "Parameter One", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "4": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Identifier + "5": { + + // Name + "Name": "Identifier", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Value + "6": { + + // Name + "Name": "Value", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Switch type + "7": { + + // Name + "Name": "Switch Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin bulletproof components + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_BULLETPROOF_COMPONENTS_MESSAGE_TYPE.toFixed()]: { + + // Tau x + "1": { + + // Name + "Name": "Tau X", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // T one + "2": { + + // Name + "Name": "T One", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // T two + "3": { + + // Name + "Name": "T Two", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin verify root public key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_VERIFY_ROOT_PUBLIC_KEY_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + } + }, + + // MimbleWimble Coin verify address + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_VERIFY_ADDRESS_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Address type + "3": { + + // Name + "Name": "Parameter One", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "4": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "5": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + } + }, + + // MimbleWimble Coin start encrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_ENCRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "4": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Recipient address + "5": { + + // Name + "Name": "Recipient Address", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin encrypted slate nonce + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_NONCE_AND_SALT_MESSAGE_TYPE.toFixed()]: { + + // Nonce + "1": { + + // Name + "Name": "Nonce", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Salt + "2": { + + // Name + "Name": "Salt", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin continue encrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_ENCRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: { + + // Data + "1": { + + // Name + "Name": "Data", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin encrypted slate data + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_DATA_MESSAGE_TYPE.toFixed()]: { + + // Encrypted data + "1": { + + // Name + "Name": "Encrypted Data", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin finish encrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_ENCRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: {}, + + // MimbleWimble Coin encrypted slate tag and signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_ENCRYPTED_SLATE_TAG_AND_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Tag + "1": { + + // Name + "Name": "Tag", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // MQS message signature + "2": { + + // Name + "Name": "MQS Message Signature", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin start decrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_DECRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "4": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Nonce + "5": { + + // Name + "Name": "Nonce", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Sender address or ephemeral X25519 public key + "6": { + + // Name + "Name": "Sender Address Or Ephemeral X25519 Public Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Salt or encrypted file key + "7": { + + // Name + "Name": "Salt Or Encrypted File Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + }, + + // Payload nonce + "8": { + + // Name + "Name": "Payload Nonce", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin continue decrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_DECRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: { + + // Encrypted data + "1": { + + // Name + "Name": "Encrypted Data", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin decrypted slate data + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_DATA_MESSAGE_TYPE.toFixed()]: { + + // Data + "1": { + + // Name + "Name": "Data", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin finish decrypting slate + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_DECRYPTING_SLATE_MESSAGE_TYPE.toFixed()]: { + + // Tag + "1": { + + // Name + "Name": "Tag", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin decrypted slate AES key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_DECRYPTED_SLATE_AES_KEY_MESSAGE_TYPE.toFixed()]: { + + // AES key + "1": { + + // Name + "Name": "AES Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin start transaction + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_START_TRANSACTION_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "4": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Output + "5": { + + // Name + "Name": "Output", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Input + "6": { + + // Name + "Name": "Input", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Fee + "7": { + + // Name + "Name": "Fee", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Secret nonce index + "8": { + + // Name + "Name": "Secret Nonce Index", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Address + "9": { + + // Name + "Name": "Address", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin continue transaction include output + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_OUTPUT_MESSAGE_TYPE.toFixed()]: { + + // Identifier + "1": { + + // Name + "Name": "Identifier", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Value + "2": { + + // Name + "Name": "Value", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Switch type + "3": { + + // Name + "Name": "Switch Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin continue transaction include input + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_INCLUDE_INPUT_MESSAGE_TYPE.toFixed()]: { + + // Identifier + "1": { + + // Name + "Name": "Identifier", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Value + "2": { + + // Name + "Name": "Value", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Switch type + "3": { + + // Name + "Name": "Switch Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin continue transaction apply offset + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_APPLY_OFFSET_MESSAGE_TYPE.toFixed()]: { + + // Offset + "1": { + + // Name + "Name": "Offset", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin transaction secret nonce index + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_SECRET_NONCE_INDEX_MESSAGE_TYPE.toFixed()]: { + + // Secret nonce index + "1": { + + // Name + "Name": "Secret Nonce Index", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin continue transaction get public key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_KEY_MESSAGE_TYPE.toFixed()]: {}, + + // MimbleWimble Coin transaction public key + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_KEY_MESSAGE_TYPE.toFixed()]: { + + // Public key + "1": { + + // Name + "Name": "Public Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin continue transaction get public nonce + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_PUBLIC_NONCE_MESSAGE_TYPE.toFixed()]: {}, + + // MimbleWimble Coin transaction public nonce + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_PUBLIC_NONCE_MESSAGE_TYPE.toFixed()]: { + + // Public nonce + "1": { + + // Name + "Name": "Public Nonce", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin continue transaction get message signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_CONTINUE_TRANSACTION_GET_MESSAGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Message + "1": { + + // Name + "Name": "Message", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin transaction message signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_MESSAGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Message signature + "1": { + + // Name + "Name": "Message Signature", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin finish transaction + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_FINISH_TRANSACTION_MESSAGE_TYPE.toFixed()]: { + + // Address type + "1": { + + // Name + "Name": "Parameter One", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Public nonce + "2": { + + // Name + "Name": "Public Nonce", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Public key + "3": { + + // Name + "Name": "Public Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Kernel information + "4": { + + // Name + "Name": "Kernel Information", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Kernel commitment + "5": { + + // Name + "Name": "Kernel Commitment", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + }, + + // Payment proof + "6": { + + // Name + "Name": "Payment Proof", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin transaction signature and payment proof + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_TRANSACTION_SIGNATURE_AND_PAYMENT_PROOF_MESSAGE_TYPE.toFixed()]: { + + // Signature + "1": { + + // Name + "Name": "Signature", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Payment proof + "2": { + + // Name + "Name": "Payment Proof", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin get MQS challenge signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_MQS_CHALLENGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Index + "4": { + + // Name + "Name": "Index", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Timestamp + "5": { + + // Name + "Name": "Timestamp", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64, + + // Optional + "Optional": true + }, + + // Time zone offset + "6": { + + // Name + "Name": "Time Zone Offset", + + // Type + "Type": ProtocolBuffers.SINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT16, + + // Optional + "Optional": true + } + }, + + // MimbleWimble Coin MQS challenge signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_MQS_CHALLENGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // MQS challenge signature + "1": { + + // Name + "Name": "MQS Challenge Signature", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin get login challenge signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_GET_LOGIN_CHALLENGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Coin type + "1": { + + // Name + "Name": "Coin Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Network type + "2": { + + // Name + "Name": "Network Type", + + // Type + "Type": ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + }, + + // Account + "3": { + + // Name + "Name": "Account", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT32 + }, + + // Timestamp + "4": { + + // Name + "Name": "Timestamp", + + // Type + "Type": ProtocolBuffers.UINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT64 + }, + + // Time zone offset + "5": { + + // Name + "Name": "Time Zone Offset", + + // Type + "Type": ProtocolBuffers.SINT_SCHEMA_DATA_TYPE, + + // Size + "Size": Common.BYTES_IN_A_UINT16 + }, + + // Identifier + "6": { + + // Name + "Name": "Identifier", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + }, + + // MimbleWimble Coin login challenge signature + [HardwareWalletDefinitions.MIMBLEWIMBLE_COIN_LOGIN_CHALLENGE_SIGNATURE_MESSAGE_TYPE.toFixed()]: { + + // Login public key + "1": { + + // Name + "Name": "Login Public Key", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + }, + + // Login challenge signature + "2": { + + // Name + "Name": "Login Challenge Signature", + + // Type + "Type": ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + } + } + }; + } + + // Ledger ignore field names + static get LEDGER_IGNORE_FIELD_NAMES() { + + // Return Ledger ignore field names + return [ + + // Coin type + "Coin Type", + + // Network type + "Network Type", + + // Parameter one + "Parameter One", + + // Parameter Two + "Parameter Two" + ]; + } + + // Private + + // Message response mask + static get MESSAGE_RESPONSE_MASK() { + + // Return message response mask + return 0x80; + } +} + + +// Main function + +// Set global object's hardware wallet definitions +globalThis["HardwareWalletDefinitions"] = HardwareWalletDefinitions; diff --git a/scripts/hardware_wallet_usb_transport.js b/scripts/hardware_wallet_usb_transport.js new file mode 100755 index 0000000..3f5f30e --- /dev/null +++ b/scripts/hardware_wallet_usb_transport.js @@ -0,0 +1,1163 @@ +// Use strict +"use strict"; + + +// Classes + +// HardwareWallet USB transport class +class HardwareWalletUsbTransport { + + // Public + + // Constructor + constructor(device, interfaceNumber) { + + // Set device + this.device = device; + + // Set interface number + this.interfaceNumber = interfaceNumber; + + // Set allow disconnect event to true + this.allowDisconnectEvent = true; + + // Set product name + var productName = device["manufacturerName"] + " " + device["productName"]; + + // Go through all devices + for(var i = 0; i < HardwareWalletUsbTransport.DEVICES["length"]; ++i) { + + // Check if device's vendor ID matches the device's + if(device["vendorId"] === HardwareWalletUsbTransport.DEVICES[i]["Vendor ID"]) { + + // Set type to device's type + this.type = HardwareWalletUsbTransport.DEVICES[i]["Type"]; + + // Check if device's product ID matches the device's + if("Product ID" in HardwareWalletUsbTransport.DEVICES[i] === false || device["productId"] === HardwareWalletUsbTransport.DEVICES[i]["Product ID"]) { + + // Check if device has a product name + if("Product Name" in HardwareWalletUsbTransport.DEVICES[i] === true) { + + // Set product name + productName = HardwareWalletUsbTransport.DEVICES[i]["Product Name"]; + } + + // Break + break; + } + } + } + + // Set device model + this["deviceModel"] = { + + // Product name + "productName": productName + }; + } + + // On + on(event, callback) { + + // Check event + switch(event) { + + // Disconnect + case "disconnect": + + // Set self + var self = this; + + // Create callback once + var callbackOnce = function(event) { + + // Check if device was disconnected + if(event["device"] === self.device) { + + // Remove USB disconnect event + navigator["usb"].removeEventListener("disconnect", callbackOnce); + + // Check if disconnect event is allowed + if(self.allowDisconnectEvent === true) { + + // Call callback + callback(); + } + } + }; + + // USB disconnect event + navigator["usb"].addEventListener("disconnect", callbackOnce); + + // Return callback once + return callbackOnce; + } + } + + // Off + off(event, callback) { + + // Check event + switch(event) { + + // Disconnect + case "disconnect": + + // Remove USB disconnect event + navigator["usb"].removeEventListener("disconnect", callback); + + // Break + break; + } + } + + // Close + close() { + + // Clear allow disconnect event + this.allowDisconnectEvent = false; + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Return releasing device interface and catch errors + return self.device.releaseInterface(self.interfaceNumber).catch(function(error) { + + // Finally + }).finally(function() { + + // Return resetting device and catch errors + return self.device.reset().catch(function(error) { + + // Finally + }).finally(function() { + + // Return closing device and catch errors + return self.device.close().then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + }); + }); + } + + // Send + send(messageType, parameterOne, parameterTwo, data) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check type + switch(self.type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Create header + var header = new Uint8Array([messageType >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, messageType, parameterOne, parameterTwo, data["length"]]); + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Create header + var header = new Uint8Array([messageType >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, messageType, data["length"] >>> (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 3), data["length"] >>> (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 2), data["length"] >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, data["length"]]); + + // Break + break; + } + + // Create message + var message = new Uint8Array(header["length"] + data["length"]); + message.set(header); + message.set(data, header["length"]); + + // Return sending message to the device + return HardwareWalletUsbTransport.sendRequest(self.device, self.type, message).then(function(response) { + + // Check if response contains a message type + if(response["length"] >= HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH) { + + // Get message type + var messageType = (response[response["length"] - HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | response[response["length"] - (HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH - 1)]; + + // Resolve + resolve({ + + // Message type + "Message Type": messageType, + + // Data + "Data": response.subarray(0, response["length"] - HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH) + }); + } + + // Otherwise + else { + + // Securely clear response + response.fill(0); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Check if error is that the device was disconnected + if(typeof error === "object" && error !== null && (("code" in error === true && error["code"] === HardwareWalletUsbTransport.NOT_FOUND_ERROR_CODE) || ("name" in error === true && error["name"] === "NotFoundError"))) { + + // Reject error + reject(new DOMException("", "NetworkError")); + } + + // Otherwise + else { + + // Reject error + reject(error); + } + }); + }); + } + + // Request + static request(device = HardwareWalletUsbTransport.NO_DEVICE) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Get device + var getDevice = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if no device was provided + if(device === HardwareWalletUsbTransport.NO_DEVICE) { + + // Return requesting USB device + return navigator["usb"].requestDevice({ + + // Filters + "filters": HardwareWalletUsbTransport.DEVICES.map(function(device) { + + // Check if device has a product ID + if("Product ID" in device === true) { + + // Return device's vendor ID and product ID + return { + + // Vendor ID + "vendorId": device["Vendor ID"], + + // Product ID + "productId": device["Product ID"] + }; + } + + // Otherwise + else { + + // Return device's vendor ID + return { + + // Vendor ID + "vendorId": device["Vendor ID"] + }; + } + }) + + }).then(function(device) { + + // Check if device isn't opened + if(device["opened"] === false) { + + // Resolve device + resolve(device); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "InvalidStateError")); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Initialize device applicable + var deviceApplicable = false; + + // Go through all devices + for(var i = 0; i < HardwareWalletUsbTransport.DEVICES["length"]; ++i) { + + // Check if device's vendor ID and product ID match the device's + if(device["vendorId"] === HardwareWalletUsbTransport.DEVICES[i]["Vendor ID"] && ("Product ID" in HardwareWalletUsbTransport.DEVICES[i] === false || device["productId"] === HardwareWalletUsbTransport.DEVICES[i]["Product ID"])) { + + // Set device applicable + deviceApplicable = true; + + // Break + break; + } + } + + // Check if device is applicable + if(deviceApplicable === true) { + + // Check if device isn't opened + if(device["opened"] === false) { + + // Resolve device + resolve(device); + } + + // Otherwise + else { + + // Reject error + reject(new DOMException("", "InvalidStateError")); + } + } + + // Otherwise + else { + + // Reject + reject(); + } + } + }); + }; + + // Return getting device + return getDevice().then(function(device) { + + // Return opening device + return device.open().then(function() { + + // Return selecting device's configuration + return device.selectConfiguration(HardwareWalletUsbTransport.CONFIGURATION).then(function() { + + // Return resetting device and catch errors + return device.reset().catch(function(error) { + + // Finally + }).finally(function() { + + // Initialize interface found + var interfaceFound = false; + + // Go through all the configuration's interfaces + for(var i = 0; i < device["configurations"][0]["interfaces"]["length"] && interfaceFound === false; ++i) { + + // Go through all of the interface's alternates + for(var j = 0; j < device["configurations"][0]["interfaces"][i]["alternates"]["length"]; ++j) { + + // Check if alternates is for WebUSB + if(device["configurations"][0]["interfaces"][i]["alternates"][j]["interfaceClass"] === HardwareWalletUsbTransport.WEBUSB_INTERFACE_CLASS) { + + // Set interface found + interfaceFound = true; + + // Set interface number + var interfaceNumber = device["configurations"][0]["interfaces"][i]["interfaceNumber"]; + + // Break + break; + } + } + } + + // Check if interface was found + if(interfaceFound === true) { + + // Return claiming interface + return device.claimInterface(interfaceNumber).then(function() { + + // Create transport for the device + var transport = new HardwareWalletUsbTransport(device, interfaceNumber); + + // Resolve transport + resolve(transport); + + // Catch errors + }).catch(function(error) { + + // Return closing device and catch errors + return device.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject error + reject(error); + }); + }); + } + + // Otherwise + else { + + // Return closing device and catch errors + return device.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject + reject(); + }); + } + }); + + // Catch errors + }).catch(function(error) { + + // Return closing device and catch errors + return device.close().catch(function(error) { + + // Finally + }).finally(function() { + + // Reject error + reject(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // List + static list() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return getting attached USB devices + return navigator["usb"].getDevices().then(function(devices) { + + // Initialize applicable devices + var applicableDevices = []; + + // Go through all devices + for(var i = 0; i < devices["length"]; ++i) { + + // Check if device isn't opened + if(devices[i]["opened"] === false) { + + // Initialize device applicable + var deviceApplicable = false; + + // Go through all devices + for(var j = 0; j < HardwareWalletUsbTransport.DEVICES["length"]; ++j) { + + // Check if device's vendor ID and product ID match the device's + if(devices[i]["vendorId"] === HardwareWalletUsbTransport.DEVICES[j]["Vendor ID"] && ("Product ID" in HardwareWalletUsbTransport.DEVICES[j] === false || devices[i]["productId"] === HardwareWalletUsbTransport.DEVICES[j]["Product ID"])) { + + // Set device aapplicable + deviceApplicable = true; + + // Break + break; + } + } + + // Check if device is applicable + if(deviceApplicable === true) { + + // Append device to list + applicableDevices.push(devices[i]); + } + } + } + + // Resolve applicable devices + resolve(applicableDevices); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // Private + + // Create packets + static createPackets(channel, type, payload = HardwareWalletUsbTransport.NO_PAYLOAD) { + + // Initialize packets + var packets = []; + + // Check if payload doesn't exist + if(payload === HardwareWalletUsbTransport.NO_PAYLOAD) { + + // Set payload to an empty array + payload = new Uint8Array([]); + } + + // Check type + switch(type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Create padded payload + var numberOfPackets = Math.ceil((HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + payload["length"]) / (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH)); + var paddedPayload = (new Uint8Array(numberOfPackets * (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH))).fill(0); + paddedPayload.set(new Uint8Array([payload["length"] >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, payload["length"]])); + paddedPayload.set(payload, Uint16Array["BYTES_PER_ELEMENT"]); + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if more than one packet will be used + if(payload["length"] > HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"]) { + + // Create padded payload + var numberOfPackets = Math.ceil((payload["length"] - (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"])) / (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"])); + var paddedPayload = (new Uint8Array(HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"] + numberOfPackets * (HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]))).fill(0); + paddedPayload.set(payload); + } + + // Otherwise + else { + + // Create padded payload + var paddedPayload = (new Uint8Array(HardwareWalletUsbTransport.PACKET_SIZE - HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"])).fill(0); + paddedPayload.set(payload); + } + + // Break + break; + } + + // Set payload to padded payload + payload = paddedPayload; + + // Initialize payload offset + var payloadOffset = 0; + + // Go through all packets required to send the payload + for(var i = 0; payloadOffset !== payload["length"]; ++i) { + + // Check type + switch(type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Create header + var header = new Uint8Array([channel >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, channel, HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_TAG, i >>> HardwareWalletUsbTransport.BITS_IN_A_BYTE, i]); + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if at the first packet + if(i === 0) { + + // Create header + var header = HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER; + } + + // Otherwise + else { + + // Create header + var header = HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER; + } + + // Break + break; + } + + // Get payload part length + var payloadPartLength = HardwareWalletUsbTransport.PACKET_SIZE - header["length"]; + + // Create packet + var packet = new Uint8Array(header["length"] + payloadPartLength); + packet.set(header); + packet.set(payload.subarray(payloadOffset, payloadOffset + payloadPartLength), header["length"]); + + // Append packet to list + packets.push(packet); + + // Update payload offset + payloadOffset += payloadPartLength; + } + + // Return packets + return packets; + } + + // Send request + static sendRequest(device, type, payload = HardwareWalletUsbTransport.NO_PAYLOAD) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Create random channel + var channel = Math.floor(Math.random() * HardwareWalletUsbTransport.MAX_CHANNEL); + + // Get packets + var packets = HardwareWalletUsbTransport.createPackets(channel, type, payload); + + // Check type + switch(type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Set endpoint + var endpoint = HardwareWalletUsbTransport.LEDGER_ENDPOINT; + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Set endpoint + var endpoint = HardwareWalletUsbTransport.TREZOR_ENDPOINT; + + // Break + break; + } + + // Send packet + var sendPacket = new Promise(function(resolve, reject) { + + // Resolve + resolve(); + }); + + // Initialize sending packets + var sendingPackets = [sendPacket]; + + // Go through all packets + for(var i = 0; i < packets["length"]; ++i) { + + // Get packet + let packet = packets[i]; + + // Send next pack after previous packet is send + sendPacket = sendPacket.then(function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return transfering out packet + return device.transferOut(endpoint, packet).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + + // Catch errors + }).catch(function(error) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Reject error + reject(error); + }); + }); + + // Append sending packet to list + sendingPackets.push(sendPacket); + } + + // Return sending all packets + return Promise.all(sendingPackets).then(function() { + + // Receive packet + var receivePacket = function(expectedSequenceIndex) { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return transfering in packet + return device.transferIn(endpoint, HardwareWalletUsbTransport.PACKET_SIZE).then(function(response) { + + // Get packet from response + var packet = new Uint8Array(response["data"]["buffer"]); + + // Check if packet's size is correct + if(packet["length"] === HardwareWalletUsbTransport.PACKET_SIZE) { + + // Check type + switch(type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Get response channel + var responseChannel = (packet[0] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | packet[1]; + + // Check if response channel is correct + if(responseChannel === channel) { + + // Check if tag is correct + if(packet[Uint16Array["BYTES_PER_ELEMENT"]] === HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_TAG) { + + // Get sequence index + var sequenceIndex = (packet[Uint16Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"]] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | packet[Uint16Array["BYTES_PER_ELEMENT"] + Uint8Array["BYTES_PER_ELEMENT"] + 1]; + + // Check if sequence index is correct + if(sequenceIndex === expectedSequenceIndex) { + + // Resolve packet's payload + resolve(packet.subarray(HardwareWalletUsbTransport.LEDGER_PACKET_HEADER_LENGTH)); + } + + // Otherwise + else { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + } + } + + // Otherwise + else { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + } + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Check if at the first packet + if(expectedSequenceIndex === 0) { + + // Get magic numbers + var magicNumbers = packet.subarray(0, HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"]); + + // Go through all magic numbers + for(var i = 0; i < magicNumbers["length"]; ++i) { + + // Check if magic number isn't correct + if(magicNumbers[i] !== HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER[i]) { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + + // Return + return; + } + } + + // Resolve packet's payload + resolve(packet.subarray(HardwareWalletUsbTransport.TREZOR_FIRST_PACKET_HEADER["length"])); + } + + // Otherwise + else { + + // Get magic numbers + var magicNumbers = packet.subarray(0, HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"]); + + // Go through all magic numbers + for(var i = 0; i < magicNumbers["length"]; ++i) { + + // Check if magic number isn't correct + if(magicNumbers[i] !== HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER[i]) { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + + // Return + return; + } + } + + // Resolve packet's payload + resolve(packet.subarray(HardwareWalletUsbTransport.TREZOR_NEXT_PACKETS_HEADER["length"])); + } + + // Break + break; + } + + } + + // Otherwise + else { + + // Securely clear packet + packet.fill(0); + + // Reject + reject(); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + }; + + // Return receiving first packet + return receivePacket(0).then(function(responsePart) { + + // Check type + switch(type) { + + // Ledger type + case HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE: + + // Get message type + var messageType = new Uint8Array([]); + + // Get response size + var responseSize = (responsePart[0] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | responsePart[1]; + + // Set response + var response = responsePart.subarray(Uint16Array["BYTES_PER_ELEMENT"]); + + // Break + break; + + // Trezor type + case HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE: + + // Get message type + var messageType = responsePart.subarray(0, HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH); + + // Get response size + var responseSize = (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH] << (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 3)) | (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 1] << (HardwareWalletUsbTransport.BITS_IN_A_BYTE * 2)) | (responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 2] << HardwareWalletUsbTransport.BITS_IN_A_BYTE) | responsePart[HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + 3]; + + // Set response + var response = responsePart.subarray(HardwareWalletUsbTransport.MESSAGE_TYPE_LENGTH + Uint32Array["BYTES_PER_ELEMENT"]); + + // Break + break; + } + + // Set next sequence index + var nextSequenceIndex = 1; + + // Get next response part + var getNextResponsePart = function() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if the entire response hasn't been received + if(response["length"] < responseSize) { + + // Return receiving next packet + return receivePacket(nextSequenceIndex).then(function(responsePart) { + + // Append response part to response + var currentResponse = new Uint8Array(response["length"] + responsePart["length"]); + currentResponse.set(response); + currentResponse.set(responsePart, response["length"]); + response.fill(0); + responsePart.fill(0); + response = currentResponse; + + // Increment next sequence index + ++nextSequenceIndex; + + // Return getting next response part + return getNextResponsePart().then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + }; + + // Return getting next response part + return getNextResponsePart().then(function() { + + // Append message type to response + var finalResponse = new Uint8Array(responseSize + messageType["length"]); + finalResponse.set(response.subarray(0, responseSize)); + finalResponse.set(messageType, responseSize); + response.fill(0); + + // Resolve final response + resolve(finalResponse); + + // Catch errors + }).catch(function(error) { + + // Securely clear response + response.fill(0); + + // Reject error + reject(error); + }); + + // Catch error + }).catch(function(error) { + + // Reject error + reject(error); + }); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + }); + } + + // No payload + static get NO_PAYLOAD() { + + // Return no payload + return null; + } + + // No device + static get NO_DEVICE() { + + // Return no device + return null; + } + + // Devices + static get DEVICES() { + + // Return devices + return [ + + // Ledger + { + + // Type + "Type": HardwareWalletDefinitions.LEDGER_TRANSPORT_TYPE, + + // Vendor ID + "Vendor ID": 0x2C97 + }, + + // Trezor + { + + // Type + "Type": HardwareWalletDefinitions.TREZOR_TRANSPORT_TYPE, + + // Product name + "Product Name": "Trezor", + + // Vendor ID + "Vendor ID": 0x1209, + + // Product ID + "Product ID": 0x53C1 + } + ]; + } + + // WebUSB interface class + static get WEBUSB_INTERFACE_CLASS() { + + // Return WebUSB interface class + return 0xFF; + } + + // Configuration + static get CONFIGURATION() { + + // Return configuration + return 0x01; + } + + // Packet size + static get PACKET_SIZE() { + + // Return packet size + return 64; + } + + // Message type length + static get MESSAGE_TYPE_LENGTH() { + + // Return message type length + return Uint16Array["BYTES_PER_ELEMENT"]; + } + + // Bits in a byte + static get BITS_IN_A_BYTE() { + + // Rerurn bits in a byte + return 8; + } + + // Ledger endpoint + static get LEDGER_ENDPOINT() { + + // Return Ledger endpoint + return 0x03; + } + + // Trezor endpoint + static get TREZOR_ENDPOINT() { + + // Return Trezor endpoint + return 0x01; + } + + // Not found error code + static get NOT_FOUND_ERROR_CODE() { + + // Return not found error code + return 8; + } + + // Ledger packet header length + static get LEDGER_PACKET_HEADER_LENGTH() { + + // Return Ledger packet header length + return 5; + } + + // Ledger packet header tag + static get LEDGER_PACKET_HEADER_TAG() { + + // Return Ledger packet header tag + return 0x05; + } + + // Trezor first packet header + static get TREZOR_FIRST_PACKET_HEADER() { + + // Return Trezor packet header + return new Uint8Array([0x3F, 0x23, 0x23]); + } + + // Trezor next packets header + static get TREZOR_NEXT_PACKETS_HEADER() { + + // Return Trezor next packets header + return new Uint8Array([0x3F]); + } + + // Max channel + static get MAX_CHANNEL() { + + // Return max channel + return 0xFFFF; + } +} + + +// Main function + +// Set global object's hardware wallet USB transport +globalThis["HardwareWalletUsbTransport"] = HardwareWalletUsbTransport; diff --git a/scripts/hash.js b/scripts/hash.js new file mode 100755 index 0000000..16487ed --- /dev/null +++ b/scripts/hash.js @@ -0,0 +1,80 @@ +// Use strict +"use strict"; + + +// Classes + +// Hash class +class Hash { + + // Public + + // Constructor + constructor(data) { + + // Check if creating hash from data was successful + this.bytes = Blake2b.compute(Hash.HASH_LENGTH, Common.mergeArrays(data), new Uint8Array([])); + + if(this.bytes === Blake2b.OPERATION_FAILED) { + + // Throw error + throw "Creating hash failed."; + } + } + + // Compare + compare(hash) { + + // Go through all bytes in the hash + for(var i = 0; i < Hash.HASH_LENGTH; ++i) { + + // Get bytes + var byte = this.getBytes()[i]; + var otherByte = hash.getBytes()[i]; + + // Check if byte is greater than the other + if(byte > otherByte) + + // Return sort greater than + return Common.SORT_GREATER_THAN; + + // Otherwise check if byte is less than the other + else if(byte < otherByte) + + // Return sort less than + return Common.SORT_LESS_THAN; + } + + // Return sort equal + return Common.SORT_EQUAL; + } + + // Serialize + serialize() { + + // Return serialized hash + return Common.toHexString(this.getBytes()); + } + + // Private + + // Get bytes + getBytes() { + + // Return bytes + return this.bytes; + } + + // Hash length + static get HASH_LENGTH() { + + // Return hash length + return 32; + } +} + + +// Main function + +// Set global object's hash +globalThis["Hash"] = Hash; diff --git a/scripts/height.js b/scripts/height.js new file mode 100755 index 0000000..005fa18 --- /dev/null +++ b/scripts/height.js @@ -0,0 +1,64 @@ +// Use strict +"use strict"; + + +// Classes + +// Height class +class Height { + + // Public + + // Constructor + constructor(height, hash = Height.NO_HASH) { + + // Set height + this.setHeight(height); + + // Set hash + this.setHash(hash); + } + + // Set height + setHeight(height) { + + // Set height + this.height = height; + } + + // Get height + getHeight() { + + // Return height + return this.height; + } + + // Set hash + setHash(hash) { + + // Set hash + this.hash = hash; + } + + // Get hash + getHash() { + + // Return hash + return this.hash; + } + + // Private + + // No hash + static get NO_HASH() { + + // Return no hash + return ""; + } +} + + +// Main function + +// Set global object's height +globalThis["Height"] = Height; diff --git a/scripts/hi-base32 license.txt b/scripts/hi-base32 license.txt new file mode 100755 index 0000000..da790a8 --- /dev/null +++ b/scripts/hi-base32 license.txt @@ -0,0 +1,22 @@ +Copyright (c) 2015-2021 Chen, Yi-Cyuan + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/scripts/hi-base32-0.5.1.js b/scripts/hi-base32-0.5.1.js new file mode 100755 index 0000000..3814239 --- /dev/null +++ b/scripts/hi-base32-0.5.1.js @@ -0,0 +1,458 @@ +/* + * [hi-base32]{@link https://github.com/emn178/hi-base32} + * + * @version 0.5.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2015-2018 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var root = typeof window === 'object' ? window : self; + var NODE_JS = !root.HI_BASE32_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } + var COMMON_JS = !root.HI_BASE32_NO_COMMON_JS && typeof module === 'object' && module.exports; + var AMD = typeof define === 'function' && define.amd; + var BASE32_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.split(''); + var BASE32_DECODE_CHAR = { + 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, + 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, + 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, + 'Z': 25, '2': 26, '3': 27, '4': 28, '5': 29, '6': 30, '7': 31 + }; + + var blocks = [0, 0, 0, 0, 0, 0, 0, 0]; + + var throwInvalidUtf8 = function (position, partial) { + if (partial.length > 10) { + partial = '...' + partial.substr(-10); + } + var err = new Error('Decoded data is not valid UTF-8.' + + ' Maybe try base32.decode.asBytes()?' + + ' Partial data after reading ' + position + ' bytes: ' + partial + ' <-'); + err.position = position; + throw err; + }; + + var toUtf8String = function (bytes) { + var str = '', length = bytes.length, i = 0, followingChars = 0, b, c; + while (i < length) { + b = bytes[i++]; + if (b <= 0x7F) { + str += String.fromCharCode(b); + continue; + } else if (b > 0xBF && b <= 0xDF) { + c = b & 0x1F; + followingChars = 1; + } else if (b <= 0xEF) { + c = b & 0x0F; + followingChars = 2; + } else if (b <= 0xF7) { + c = b & 0x07; + followingChars = 3; + } else { + throwInvalidUtf8(i, str); + } + + for (var j = 0; j < followingChars; ++j) { + b = bytes[i++]; + if (b < 0x80 || b > 0xBF) { + throwInvalidUtf8(i, str); + } + c <<= 6; + c += b & 0x3F; + } + if (c >= 0xD800 && c <= 0xDFFF) { + throwInvalidUtf8(i, str); + } + if (c > 0x10FFFF) { + throwInvalidUtf8(i, str); + } + + if (c <= 0xFFFF) { + str += String.fromCharCode(c); + } else { + c -= 0x10000; + str += String.fromCharCode((c >> 10) + 0xD800); + str += String.fromCharCode((c & 0x3FF) + 0xDC00); + } + } + return str; + }; + + var decodeAsBytes = function (base32Str) { + if (base32Str === '') { + return []; + } else if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error('Invalid base32 characters'); + } + base32Str = base32Str.replace(/=/g, ''); + var v1, v2, v3, v4, v5, v6, v7, v8, bytes = [], index = 0, length = base32Str.length; + + // 4 char to 3 bytes + for (var i = 0, count = length >> 3 << 3; i < count;) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255; + bytes[index++] = (v7 << 5 | v8) & 255; + } + + // remain bytes + var remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255; + } + return bytes; + }; + + var encodeAscii = function (str) { + var v1, v2, v3, v4, v5, base32Str = '', length = str.length; + for (var i = 0, count = parseInt(length / 5) * 5; i < count;) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i++); + v5 = str.charCodeAt(i++); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + var remain = length - count; + if (remain === 1) { + v1 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (remain === 2) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (remain === 3) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else if (remain === 4) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + return base32Str; + }; + + var encodeUtf8 = function (str) { + var v1, v2, v3, v4, v5, code, end = false, base32Str = '', + index = 0, i, start = 0, bytes = 0, length = str.length; + if (str === '') { + return base32Str; + } + do { + blocks[0] = blocks[5]; + blocks[1] = blocks[6]; + blocks[2] = blocks[7]; + for (i = start; index < length && i < 5; ++index) { + code = str.charCodeAt(index); + if (code < 0x80) { + blocks[i++] = code; + } else if (code < 0x800) { + blocks[i++] = 0xc0 | (code >> 6); + blocks[i++] = 0x80 | (code & 0x3f); + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i++] = 0xe0 | (code >> 12); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff)); + blocks[i++] = 0xf0 | (code >> 18); + blocks[i++] = 0x80 | ((code >> 12) & 0x3f); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } + } + bytes += i - start; + start = i - 5; + if (index === length) { + ++index; + } + if (index > length && i < 6) { + end = true; + } + v1 = blocks[0]; + if (i > 4) { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + v5 = blocks[4]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } else if (i === 1) { + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (i === 2) { + v2 = blocks[1]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (i === 3) { + v2 = blocks[1]; + v3 = blocks[2]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + } while (!end); + return base32Str; + }; + + var encodeBytes = function (bytes) { + var v1, v2, v3, v4, v5, base32Str = '', length = bytes.length; + for (var i = 0, count = parseInt(length / 5) * 5; i < count;) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i++]; + v5 = bytes[i++]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + var remain = length - count; + if (remain === 1) { + v1 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (remain === 2) { + v1 = bytes[i++]; + v2 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (remain === 3) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else if (remain === 4) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + return base32Str; + }; + + var encode = function (input, asciiOnly) { + var notString = typeof(input) !== 'string'; + if (notString && input.constructor === ArrayBuffer) { + input = new Uint8Array(input); + } + if (notString) { + return encodeBytes(input); + } else if (asciiOnly) { + return encodeAscii(input); + } else { + return encodeUtf8(input); + } + }; + + var decode = function (base32Str, asciiOnly) { + if (!asciiOnly) { + return toUtf8String(decodeAsBytes(base32Str)); + } + if (base32Str === '') { + return ''; + } else if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error('Invalid base32 characters'); + } + var v1, v2, v3, v4, v5, v6, v7, v8, str = '', length = base32Str.indexOf('='); + if (length === -1) { + length = base32Str.length; + } + + // 8 char to 5 bytes + for (var i = 0, count = length >> 3 << 3; i < count;) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) + + String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255) + + String.fromCharCode((v7 << 5 | v8) & 255); + } + + // remain bytes + var remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255); + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255); + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255); + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) + + String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255); + } + return str; + }; + + var exports = { + encode: encode, + decode: decode + }; + decode.asBytes = decodeAsBytes; + + if (COMMON_JS) { + module.exports = exports; + } else { + root.base32 = exports; + if (AMD) { + define(function() { + return exports; + }); + } + } +})(); diff --git a/scripts/identifier.js b/scripts/identifier.js new file mode 100755 index 0000000..8859be6 --- /dev/null +++ b/scripts/identifier.js @@ -0,0 +1,437 @@ +// Use strict +"use strict"; + + +// Classes + +// Identifier class +class Identifier { + + // Public + + // Constructor + constructor(serializedIdentifier = Identifier.DEFAULT_SERIALIZED_IDENTIFIER) { + + // Set depth + this.depth = 0; + + // Set paths + this.paths = new Uint32Array(Identifier.MAX_DEPTH); + + // Set value to serialized identifier + this.setValue(serializedIdentifier); + } + + // Get depth + getDepth() { + + // Return depth + return this.depth; + } + + // Get paths + getPaths() { + + // Return paths + return this.paths; + } + + // Set value + setValue(serializedIdentifierOrDepth, firstValue, secondValue, thirdValue, fourthValue) { + + // Check if value is provided in a serialized format + if(typeof serializedIdentifierOrDepth === "string") { + + // Get serialized identifier + var serializedIdentifier = serializedIdentifierOrDepth; + + // Check if serialized identifier ia a hex string + if(Common.isHexString(serializedIdentifier) === true) { + + // Get serialized value + var serializedValue = Common.fromHexString(serializedIdentifier); + + // Check if serialized value is valid + if(serializedValue["length"] === Identifier.LENGTH) { + + // Set depth + this.depth = Math.min(serializedValue[Identifier.DEPTH_INDEX], Identifier.MAX_DEPTH); + + // Go through all paths + var serializedValueDataView = new DataView(serializedValue["buffer"]); + + for(var i = 0; i < this.getPaths()["length"]; ++i) + + // Set path + this.paths[i] = serializedValueDataView.getUint32(Identifier.PATHS_INDEX + i * Uint32Array["BYTES_PER_ELEMENT"], false); + } + + // Otherwise + else { + + // Throw error + throw "Invalid identifier."; + } + } + + // Otherwise + else { + + // Throw error + throw "Invalid identifier."; + } + } + + // Otherwise + else { + + // Get depth + var depth = serializedIdentifierOrDepth; + + // Check if depth is valid + if(depth <= Identifier.MAX_DEPTH) { + + // Set depth + this.depth = depth; + + // Check if paths are provided as an array + if(firstValue instanceof Uint32Array === true) { + + // Get paths + var paths = firstValue; + + // Check if paths are valid + if(paths["length"] === this.getPaths()["length"]) { + + // Go through all paths + for(var i = 0; i < this.getPaths()["length"]; ++i) { + + // Set path to path + this.paths[i] = paths[i]; + } + } + + // Otherwise + else { + + // Throw error + throw "Invalid identifier."; + } + } + + // Otherwise + else { + + // Set paths + this.paths[0] = firstValue; + this.paths[1] = secondValue; + this.paths[2] = thirdValue; + this.paths[3] = fourthValue; + } + } + + // Otherwise + else { + + // Throw error + throw "Invalid identifier."; + } + } + } + + // Get value + getValue() { + + // Initialize buffer + var buffer = new Uint8Array(Identifier.LENGTH); + + // Set buffer's depth + buffer[Identifier.DEPTH_INDEX] = this.getDepth(); + + // Go through all paths + var bufferView = new DataView(buffer["buffer"]); + + for(var i = 0; i < this.getPaths()["length"]; ++i) + + // Set buffer's path + bufferView.setUint32(Identifier.PATHS_INDEX + i * Uint32Array["BYTES_PER_ELEMENT"], this.getPaths()[i], false); + + // Return buffer + return buffer; + } + + // Equals value + equalsValue(identifier) { + + // Check if depths differ + if(this.getDepth() !== identifier.getDepth()) + + // Return false + return false; + + // Return if paths are equal + return Common.arraysAreEqual(this.getPaths(), identifier.getPaths() === true); + } + + // Includes value + includesValue(identifier) { + + // Check if depths differ + if(this.getDepth() !== identifier.getDepth()) + + // Return false + return false; + + // Return if last path is greater than or equal to the value's last path + return this.getLastPath() >= identifier.getLastPath(); + } + + // Get last path index + getLastPath() { + + // Return last path + return (this.getDepth() === 0) ? 0 : this.getPaths()[this.getDepth() - 1]; + } + + // Get next + getNext(height = Identifier.NO_HEIGHT) { + + // Create next as a clone of self + var next = this.clone(); + + // Increment next's last path to value + ++next.paths[next.getDepth() - 1]; + + // Check if height exists + if(height !== Identifier.NO_HEIGHT) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Set next's height path as the height limited by its maximum value + next.paths[Identifier.HEIGHT_PATH_INDEX] = Math.max(height.modulo(Identifier.MAXIMUM_HEIGHT + 1).toNumber(), 1); + + // Break + break; + } + } + + // Return next + return next; + } + + // Get parent + getParent() { + + // Create parent as a clone of self + var parent = this.clone(); + + // Check if previous depth exists + if(this.getDepth() > 0) { + + // Clear parent's last path + parent.paths[parent.getDepth() - 1] = 0; + + // Decrement parent's depth + --parent.depth; + } + + // Return parent + return parent; + } + + // Get child + getChild(height = Identifier.NO_HEIGHT) { + + // Create child as a clone of self + var child = this.clone(); + + // Check if next depth exists + if(this.getDepth() < Identifier.MAX_DEPTH) { + + // Increment child's depth + ++child.depth; + } + + // Check if height exists + if(height !== Identifier.NO_HEIGHT) { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Set child's height path as the height limited by its maximum value + child.paths[Identifier.HEIGHT_PATH_INDEX] = Math.max(height.modulo(Identifier.MAXIMUM_HEIGHT + 1).toNumber(), 1); + + // Break + break; + } + } + + // Return child + return child; + } + + // Get height + getHeight() { + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Get height from height path + var height = this.getPaths()[Identifier.HEIGHT_PATH_INDEX]; + + // Check if height exists + if(height !== Consensus.FIRST_BLOCK_HEIGHT) { + + // Return height + return new BigNumber(height); + } + + // Otherwise + else { + + // Return no height + return Identifier.NO_HEIGHT; + } + } + + // Return no height + return Identifier.NO_HEIGHT; + } + + // Remove extras + removeExtras() { + + // Create copy as a clone of self + var copy = this.clone(); + + // Check wallet type + switch(Consensus.getWalletType()) { + + // MWC wallet + case Consensus.MWC_WALLET_TYPE: + + // Set copy's height path to not exist + copy.paths[Identifier.HEIGHT_PATH_INDEX] = Consensus.FIRST_BLOCK_HEIGHT; + + // Break + break; + } + + // Return copy + return copy; + } + + // Is path hardened + static isPathHardened(path) { + + // Return if path is hardened + return (path & Identifier.PATH_HARDENED_MASK) !== 0; + } + + // Length + static get LENGTH() { + + // Return length + return 1 * Uint8Array["BYTES_PER_ELEMENT"] + Identifier.MAX_DEPTH * Uint32Array["BYTES_PER_ELEMENT"]; + } + + // Max depth + static get MAX_DEPTH() { + + // Return max depth + return 4; + } + + // Root serialized identifier + static get ROOT_SERIALIZED_IDENTIFIER() { + + // Return root serialized identifier + return "0000000000000000000000000000000000"; + } + + // Paths index + static get PATHS_INDEX() { + + // Return paths index + return Identifier.DEPTH_INDEX + 1; + } + + // No identifier + static get NO_IDENTIFIER() { + + // Return no identifier + return null; + } + + // No heights + static get NO_HEIGHT() { + + // Return no height + return null; + } + + // Maximum height + static get MAXIMUM_HEIGHT() { + + // Return maximum height + return Common.UINT32_MAX_VALUE; + } + + // Private + + // Clone + clone() { + + // Create a copy of self + var copy = new Identifier(); + + copy.setValue(this.getDepth(), this.getPaths()); + + // Return copy + return copy; + } + + // Default serialized identifier + static get DEFAULT_SERIALIZED_IDENTIFIER() { + + // Return default serialized identifier + return "0200000000000000000000000000000000"; + } + + // Depth index + static get DEPTH_INDEX() { + + // Return depth index + return 0; + } + + // Path hardened mask + static get PATH_HARDENED_MASK() { + + // Return path hardened mask + return 0x80000000; + } + + // Height path index + static get HEIGHT_PATH_INDEX() { + + // Return height path index + return Identifier.MAX_DEPTH - 1; + } +} + + +// Main function + +// Set global object's identifier +globalThis["Identifier"] = Identifier; diff --git a/scripts/initial_heights_obtained.js b/scripts/initial_heights_obtained.js new file mode 100755 index 0000000..e6cacd1 --- /dev/null +++ b/scripts/initial_heights_obtained.js @@ -0,0 +1,132 @@ +// Use strict +"use strict"; + + +// Classes + +// Initial heights obtained class +class InitialHeightsObtained { + + // Public + + // Constructor + constructor(node) { + + // Create database + Database.createDatabase(function(database, currentVersion, databaseTransaction) { + + // Create or get initial heights obtained object store + var initialHeightsObtainedObjectStore = (currentVersion === Database.NO_CURRENT_VERSION) ? database.createObjectStore(InitialHeightsObtained.OBJECT_STORE_NAME, { + + // Key path + "keyPath": [ + + // Wallet type + Database.toKeyPath(InitialHeightsObtained.DATABASE_WALLET_TYPE_NAME), + + // Network type + Database.toKeyPath(InitialHeightsObtained.DATABASE_NETWORK_TYPE_NAME) + ] + + }) : databaseTransaction.objectStore(InitialHeightsObtained.OBJECT_STORE_NAME); + }); + } + + // Get obtained + getObtained() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Return getting the initial heights obtained with the wallet type and network type in the database + return Database.getResults(InitialHeightsObtained.OBJECT_STORE_NAME, 0, 1, Database.NO_INDEX, IDBKeyRange.only([ + + // Wallet type + Consensus.getWalletType(), + + // Network type + Consensus.getNetworkType(), + + ])).then(function(results) { + + // Check if initial heights were obtained + if(results["length"] !== 0) { + + // Resolve true + resolve(true); + } + + // Otherwise + else { + + // Resolve false + resolve(false); + } + + // Catch errors + }).catch(function(error) { + + // Reject error + reject("The database failed."); + }); + }); + } + + // Set obtained + setObtained() { + + // Return promise + return new Promise(function(resolve, reject) { + + // Save initial heights obtained for the wallet type and network type in the database + Database.saveResults(InitialHeightsObtained.OBJECT_STORE_NAME, [{ + + // Wallet type + [Database.toKeyPath(InitialHeightsObtained.DATABASE_WALLET_TYPE_NAME)]: Consensus.getWalletType(), + + // Network type + [Database.toKeyPath(InitialHeightsObtained.DATABASE_NETWORK_TYPE_NAME)]: Consensus.getNetworkType() + + }], [], Database.CREATE_NEW_TRANSACTION, Database.STRICT_DURABILITY).then(function() { + + // Resolve + resolve(); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject("The database failed."); + }); + }); + } + + // Private + + // Object store name + static get OBJECT_STORE_NAME() { + + // Return object store name + return "Initial Heights Obtained"; + } + + // Database wallet type name + static get DATABASE_WALLET_TYPE_NAME() { + + // Return database wallet type name + return "Wallet Type"; + } + + // Database network type name + static get DATABASE_NETWORK_TYPE_NAME() { + + // Return database network type name + return "Network Type"; + } +} + + +// Main function + +// Set global object's initial heights obtained +globalThis["InitialHeightsObtained"] = InitialHeightsObtained; diff --git a/scripts/install_app.js b/scripts/install_app.js new file mode 100755 index 0000000..39f746d --- /dev/null +++ b/scripts/install_app.js @@ -0,0 +1,353 @@ +// Use strict +"use strict"; + + +// Classes + +// Install app class +class InstallApp { + + // Public + + // Constructor + constructor(cookieAcceptance, automaticLock) { + + // Set automatic lock + this.automaticLock = automaticLock; + + // Get install app display + this.installAppDisplay = $("section.installApp"); + + // Get install now button + this.installNowButton = this.installAppDisplay.find("button.installNow"); + + // Get remind me later button + this.remindMeLaterButton = this.installAppDisplay.find("button.remindMeLater"); + + // Set can show + this.canShow = false; + + // Set was installed + this.wasInstalled = false; + + // Set cookie acceptance is hidden + this.cookieAcceptanceIsHidden = false; + + // Set self + var self = this; + + // Cookie acceptance is hidden event + $(cookieAcceptance).one(CookieAcceptance.IS_HIDDEN_EVENT, function() { + + // Set timeout + setTimeout(function() { + + // Set cookie acceptance is hidden + self.cookieAcceptanceIsHidden = true; + + // Can if can show + if(self.canShow === true) + + // Show + self.show(); + + }, InstallApp.COOKIE_ACCEPTANCE_HIDE_BEFORE_SHOW_DELAY_MILLISECONDS); + }); + + // Window before install prompt event + $(window).on("beforeinstallprompt", function(event) { + + // Prevent default + event.preventDefault(); + + // Store install app prompt + installAppPrompt = event["originalEvent"]; + + // Can if can show + if(self.canShow === true) + + // Show + self.show(); + }); + + // Install app display transaition end event + this.installAppDisplay.on("transitionend", function() { + + // Check if install app display is hiding + if(self.installAppDisplay.hasClass("hide") === true) + + // Prevent focus on install app display's elements + self.installAppDisplay.addClass("noFocus"); + }); + + // Window app installed event + $(window).on("appinstalled", function(event) { + + // Hide + self.hide(); + }); + + // Window storage event + $(window).on("storage", function(event) { + + // Check if remind me later timestamp was changed + if(event["originalEvent"]["key"] === InstallApp.INSTALL_APP_REMIND_ME_LATER_TIMESTAMP_LOCAL_STORAGE_NAME) + + // Hide + self.hide(); + }); + + // Install now button click event + this.installNowButton.on("click", function() { + + // Check if the install app prompt exists + if(installAppPrompt !== InstallApp.NO_INSTALL_APP_PROMPT) { + + // Get if automatic lock is enabled + var automaticLockEnabled = self.automaticLock.getAllowed() !== 0; + + // Check if automatic lock is enabled + if(automaticLockEnabled === true) { + + // Prevent automatic lock + self.automaticLock.prevent(); + } + + // Prompt to install app + installAppPrompt.prompt(); + + // Get if app was installed + installAppPrompt["userChoice"].then(function(result) { + + // Check if automatic lock is enabled + if(automaticLockEnabled === true) { + + // Allow automatic lock + self.automaticLock.allow(); + } + + // Check if app was installed + if(result["outcome"] === InstallApp.APP_INSTALLED_OUTCOME) { + + // Set was installed + self.wasInstalled = true; + + // Hide + self.hide(); + } + + // Catch errors + }).catch(function(error) { + + // Check if automatic lock is enabled + if(automaticLockEnabled === true) { + + // Allow automatic lock + self.automaticLock.allow(); + } + }); + } + }); + + // Remind me later button click event + this.remindMeLaterButton.on("click", function() { + + // Hide + self.hide(); + + // Try + try { + + // Save current timestamp as the remind me later timestamp + localStorage.setItem(InstallApp.INSTALL_APP_REMIND_ME_LATER_TIMESTAMP_LOCAL_STORAGE_NAME, Common.getCurrentTimestamp().toFixed()); + } + + // Catch errors + catch(error) { + + // Trigger a fatal error + new FatalError(FatalError.LOCAL_STORAGE_ERROR); + } + + // Remind me later hover in event + }).hover(function() { + + // Check if can hover + if(typeof matchMedia === "function" && matchMedia("(any-hover: hover)")["matches"] === true) { + + // Get element + var element = $(this); + + // Check if element's text is shown + if(element.children().is(":visible") === true) { + + // Save element's title + element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title", element.attr("title")); + + // Remove element's title + element.removeAttr("title"); + } + } + + // Remind me later hover out event + }, function() { + + // Check if can hover + if(typeof matchMedia === "function" && matchMedia("(any-hover: hover)")["matches"] === true) { + + // Get element + var element = $(this); + + // Check if element isn't focused + if(element.is(":focus") === false) { + + // Check if element's title is saved + if(element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title") !== Common.NO_ATTRIBUTE) { + + // Restore element's title + element.attr("title", element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title")); + + // Remove element's saved title + element.removeAttr(Common.DATA_ATTRIBUTE_PREFIX + "title"); + } + } + } + + // Remind me later focus event + }).on("focus", function() { + + // Get element + var element = $(this); + + // Check if element's text is shown + if(element.children().is(":visible") === true) { + + // Save element's title + element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title", element.attr("title")); + + // Remove element's title + element.removeAttr("title"); + } + + // Remind me later blur event + }).on("blur", function() { + + // Get element + var element = $(this); + + // Check if can't hover or element isn't hovered + if((typeof matchMedia !== "function" || matchMedia("(any-hover: hover)")["matches"] === false) || element.is(":hover") === false) { + + // Check if element's title is saved + if(element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title") !== Common.NO_ATTRIBUTE) { + + // Restore element's title + element.attr("title", element.attr(Common.DATA_ATTRIBUTE_PREFIX + "title")); + + // Remove element's saved title + element.removeAttr(Common.DATA_ATTRIBUTE_PREFIX + "title"); + } + } + }); + } + + // Show + show() { + + // Set can show + this.canShow = true; + + // Check if not an app or extension and not loading from file + if(Common.isApp() === false && Common.isExtension() === false && location["protocol"] !== Common.FILE_PROTOCOL) { + + // Check if the install app prompt exists, cookie acceptance is hidden, and wasn't installed + if(installAppPrompt !== InstallApp.NO_INSTALL_APP_PROMPT && this.cookieAcceptanceIsHidden === true && this.wasInstalled === false) { + + // Get remind me later timestamp + var remindMeLaterTimestamp = localStorage.getItem(InstallApp.INSTALL_APP_REMIND_ME_LATER_TIMESTAMP_LOCAL_STORAGE_NAME); + + // Check if it's time to remind about installing the app + if(remindMeLaterTimestamp === Common.INVALID_LOCAL_STORAGE_ITEM || parseInt(remindMeLaterTimestamp, Common.DECIMAL_NUMBER_BASE) <= Common.getCurrentTimestamp() - InstallApp.REMIND_ME_LATER_DURATION_SECONDS) { + + // Show install app display and make it so that its elements can be focused + this.installAppDisplay.removeClass("hide noFocus"); + + // Return true + return true; + } + } + } + + // Return false + return false; + } + + // No install app prompt + static get NO_INSTALL_APP_PROMPT() { + + // Return no install app prompt + return null; + } + + // Private + + // Hide + hide() { + + // Set install app prompt to no install app prompt + this.installAppPrompt = InstallApp.NO_INSTALL_APP_PROMPT; + + // Hide install app display + this.installAppDisplay.addClass("hide"); + } + + // Remind me later duration seconds + static get REMIND_ME_LATER_DURATION_SECONDS() { + + // Return remind me later duration seconds + return 90 * Common.HOURS_IN_A_DAY * Common.MINUTES_IN_AN_HOUR * Common.SECONDS_IN_A_MINUTE; + } + + // Install app remind me later timestamp local storage name + static get INSTALL_APP_REMIND_ME_LATER_TIMESTAMP_LOCAL_STORAGE_NAME() { + + // Return install app remind me later timestamp local storage name + return "Install App Remind Me Later Timestamp"; + } + + // App install outcome + static get APP_INSTALLED_OUTCOME() { + + // Return app installed outcome + return "accepted"; + } + + // Return cookie acceptance hide before show delay milliseconds + static get COOKIE_ACCEPTANCE_HIDE_BEFORE_SHOW_DELAY_MILLISECONDS() { + + // Return cookie acceptance hide before show delay milliseconds + return 100; + } +} + + +// Global variables + +// Install app prompt +var installAppPrompt = InstallApp.NO_INSTALL_APP_PROMPT; + + +// Main function + +// Set global object's install app +globalThis["InstallApp"] = InstallApp; + +// Window before install prompt event +$(window).on("beforeinstallprompt", function(event) { + + // Prevent default + event.preventDefault(); + + // Store install app prompt + installAppPrompt = event["originalEvent"]; +}); diff --git a/scripts/instance.js b/scripts/instance.js new file mode 100755 index 0000000..75e3c0e --- /dev/null +++ b/scripts/instance.js @@ -0,0 +1,268 @@ +// Use strict +"use strict"; + + +// Classes + +// Instance class +class Instance { + + // Public + + // Constructor + constructor() { + + // Set current instance ID to random value + this.currentInstanceId = Math.random().toFixed(Instance.MAX_FIXED_NUMBER_OF_DECIMAL_PLACES); + + // Set primary instance ID to stored value + this.primaryInstanceId = localStorage.getItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME); + + // Set check primary instance timeout + this.checkPrimaryInstanceTimeout = Instance.NO_TIMEOUT; + + // Set self + var self = this; + + // Try + try { + + // Save current instance ID as the primary instance ID + localStorage.setItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME, this.currentInstanceId); + } + + // Catch errors + catch(error) { + + // Throw error + throw "Local storage failed."; + } + + // Set check primary instance timeout + this.checkPrimaryInstanceTimeout = setTimeout(function() { + + // Check if primary instance + self.checkIfPrimaryInstance(); + + }, Instance.CHECK_IF_PRIMARY_INSTANCE_DELAY_MILLISECONDS); + + // Window storage instance event + $(window).on("storage.instance", function(event) { + + // Check if primary instance ID was changed + if(event["originalEvent"]["key"] === Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME) { + + // Check if primary instance was closed + if(event["originalEvent"]["newValue"] === null || event["originalEvent"]["newValue"] === "") { + + // Try + try { + + // Save current instance ID as the primary instance ID + localStorage.setItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME, self.currentInstanceId); + } + + // Catch errors + catch(error) { + + // Return + return; + } + + // Check if check primary instance timeout exists + if(self.checkPrimaryInstanceTimeout !== Instance.NO_TIMEOUT) { + + // Clear check primary instance timeout + clearTimeout(self.checkPrimaryInstanceTimeout); + + // Clear check primary instance timeout + self.checkPrimaryInstanceTimeout = Instance.NO_TIMEOUT; + } + + // Set check primary instance timeout + self.checkPrimaryInstanceTimeout = setTimeout(function() { + + // Check if primary instance + self.checkIfPrimaryInstance(); + + }, Instance.CHECK_IF_PRIMARY_INSTANCE_DELAY_MILLISECONDS); + } + + // Otherwise check if current instance ID is the primary instance ID and a new instance is trying to become the primary instance + else if(self.currentInstanceId === self.primaryInstanceId && event["originalEvent"]["newValue"] !== self.currentInstanceId) { + + // Try + try { + + // Save current instance ID as the primary instance ID + localStorage.setItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME, self.currentInstanceId); + } + + // Catch errors + catch(error) { + + // Trigger a fatal error + new FatalError(FatalError.LOCAL_STORAGE_ERROR); + } + } + + // Otherwise check if new instance isn't the current instance ID + else if(event["originalEvent"]["newValue"] !== self.currentInstanceId) + + // Update primary instance ID + self.primaryInstanceId = event["originalEvent"]["newValue"]; + } + + // Window focus instance event + }).on("focus.instance", function() { + + // Check if current instance ID isn't the primary instance ID + if(self.currentInstanceId !== self.primaryInstanceId) { + + // Set timeout + setTimeout(function() { + + // Try + try { + + // Save current instance ID as the primary instance ID + localStorage.setItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME, self.currentInstanceId); + } + + // Catch errors + catch(error) { + + // Return + return; + } + + // Check if check primary instance timeout exists + if(self.checkPrimaryInstanceTimeout !== Instance.NO_TIMEOUT) { + + // Clear check primary instance timeout + clearTimeout(self.checkPrimaryInstanceTimeout); + + // Clear check primary instance timeout + self.checkPrimaryInstanceTimeout = Instance.NO_TIMEOUT; + } + + // Set check primary instance timeout + self.checkPrimaryInstanceTimeout = setTimeout(function() { + + // Check if primary instance + self.checkIfPrimaryInstance(); + + }, Instance.CHECK_IF_PRIMARY_INSTANCE_DELAY_MILLISECONDS); + + }, Instance.FOCUS_DELAY_MILLISECONDS); + } + + // Window before unload instance and unload instance event + }).on("beforeunload.instance unload.instance", function() { + + // Turn off window before unload instance and unload instance event + $(window).off("beforeunload.instance unload.instance"); + + // Turn off window storage instance event + $(window).off("storage.instance"); + + // Turn off window focus instance event + $(window).off("focus.instance"); + + // Check if current instance ID is the primary instance ID + if(self.currentInstanceId === self.primaryInstanceId) + + // Remove saved primary instance ID + localStorage.removeItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME); + }); + } + + // Is primary instance + isPrimaryInstance() { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Set timeout + setTimeout(function() { + + // Resolve if current instance ID is the primary instance ID + resolve(self.currentInstanceId === self.primaryInstanceId); + + }, Instance.CHECK_IF_PRIMARY_INSTANCE_DELAY_MILLISECONDS); + }); + } + + // Is primary instance event + static get IS_PRIMARY_INSTANCE_EVENT() { + + // Return is primary instance event + return "InstanceIsPrimaryInstanceEvent"; + } + + // Private + + // Check if primary instance + checkIfPrimaryInstance() { + + // Clear check primary instance timeout + this.checkPrimaryInstanceTimeout = Instance.NO_TIMEOUT; + + // Get the saved primary instance ID + var savedPrimaryInstanceId = localStorage.getItem(Instance.PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME); + + // Check if current instance ID is the saved primary instance ID + if(savedPrimaryInstanceId !== Common.INVALID_LOCAL_STORAGE_ITEM && this.currentInstanceId === savedPrimaryInstanceId) { + + // Set primary instance ID to current instance ID + this.primaryInstanceId = this.currentInstanceId; + + // Trigger is primary instance event + $(this).trigger(Instance.IS_PRIMARY_INSTANCE_EVENT); + } + } + + // Primary instance ID local storage name + static get PRIMARY_INSTANCE_ID_LOCAL_STORAGE_NAME() { + + // Return primary instance ID local storage name + return "Primary Instance ID"; + } + + // Check if primary instance delay milliseconds + static get CHECK_IF_PRIMARY_INSTANCE_DELAY_MILLISECONDS() { + + // Return check if primary instance delay milliseconds + return 600; + } + + // Focus delay milliseconds + static get FOCUS_DELAY_MILLISECONDS() { + + // Return focus delay milliseconds + return 100; + } + + // No timeout + static get NO_TIMEOUT() { + + // Return no timeout + return null; + } + + // Max fixed number of decimal places + static get MAX_FIXED_NUMBER_OF_DECIMAL_PLACES() { + + // Return max fixed number of decimal places + return 20; + } +} + + +// Main function + +// Set global object's instance +globalThis["Instance"] = Instance; diff --git a/scripts/interaction.js b/scripts/interaction.js new file mode 100755 index 0000000..6bf0752 --- /dev/null +++ b/scripts/interaction.js @@ -0,0 +1,226 @@ +// Use strict +"use strict"; + + +// Classes + +// Interaction class +class Interaction { + + // Public + + // Constructor + constructor(index, urlOrWalletKeyPath, api, type, data, listener = Interaction.NO_LISTENER) { + + // Check if a URL is provided + if(typeof urlOrWalletKeyPath === "string") { + + // Set URL + var url = urlOrWalletKeyPath; + + // Set wallet key path + var walletKeyPath = Interaction.NO_WALLET_KEY_PATH; + } + + // Otherwise + else { + + // Set URL + var url = Interaction.NO_URL; + + // Set wallet key path + var walletKeyPath = urlOrWalletKeyPath; + } + + // Set index + this.index = index; + + // Set URL + this.url = url; + + // Set wallet key path + this.walletKeyPath = walletKeyPath; + + // Set API + this.api = api; + + // Set type + this.type = type; + + // Set data + this.data = data; + + // Set listener + this.listener = listener; + + // Set canceled + this.canceled = false; + } + + // Get URL + getUrl() { + + // Return URL + return this.url; + } + + // Get wallet key path + getWalletKeyPath() { + + // Return wallet key path + return this.walletKeyPath; + } + + // Get API + getApi() { + + // Return API + return this.api; + } + + // Get type + getType() { + + // Return type + return this.type; + } + + // Get data + getData() { + + // Return data + return this.data; + } + + // Set canceled + setCanceled() { + + // Set canceled + this.canceled = true; + } + + // Is canceled + isCanceled() { + + // Return if canceled + return this.canceled === true; + } + + // Respond + respond(response) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if canceled + if(self.isCanceled() === true) { + + // Reject error + reject("Interaction canceled."); + } + + // Otherwise check if a listener is used + else if(self.listener !== Interaction.NO_LISTENER) { + + // Return responding with data to listener + return self.listener.respondWithData(self.index, response).then(function(status) { + + // Resolve status + resolve(status); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + } + + // Cancel + cancel(response) { + + // Set self + var self = this; + + // Return promise + return new Promise(function(resolve, reject) { + + // Check if canceled + if(self.isCanceled() === true) { + + // Reject error + reject("Interaction canceled."); + } + + // Otherwise check if a listener is used + else if(self.listener !== Interaction.NO_LISTENER) { + + // Return responding with error to listener + return self.listener.respondWithError(self.index, response).then(function(status) { + + // Resolve status + resolve(status); + + // Catch errors + }).catch(function(error) { + + // Reject error + reject(error); + }); + } + + // Otherwise + else { + + // Resolve + resolve(); + } + }); + } + + // No index + static get NO_INDEX() { + + // Return no index + return null; + } + + // No listener + static get NO_LISTENER() { + + // Return no listener + return null; + } + + // No URL + static get NO_URL() { + + // Return no URL + return null; + } + + // No wallet key path + static get NO_WALLET_KEY_PATH() { + + // Return no wallet key path + return null; + } +} + + +// Main function + +// Set global object's interaction +globalThis["Interaction"] = Interaction; diff --git a/scripts/jQuery license.txt b/scripts/jQuery license.txt new file mode 100755 index 0000000..f642c3f --- /dev/null +++ b/scripts/jQuery license.txt @@ -0,0 +1,20 @@ +Copyright OpenJS Foundation and other contributors, https://openjsf.org/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/scripts/jQuery-3.6.4.js b/scripts/jQuery-3.6.4.js new file mode 100755 index 0000000..7f35c11 --- /dev/null +++ b/scripts/jQuery-3.6.4.js @@ -0,0 +1,10965 @@ +/*! + * jQuery JavaScript Library v3.6.4 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2023-03-08T15:28Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket trac-14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.6.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.10 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2023-02-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Make sure the the `:has()` argument is parsed unforgivingly. + // We include `*` in the test to detect buggy implementations that are + // _selectively_ forgiving (specifically when the list includes at least + // one valid selector). + // Note that we treat complete lack of support for `:has()` as if it were + // spec-compliant support, which is fine because use of `:has()` in such + // environments will fail in the qSA path and fall back to jQuery traversal + // anyway. + support.cssHas = assert( function() { + try { + document.querySelector( ":has(*,:jqfake)" ); + return false; + } catch ( e ) { + return true; + } + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + if ( !support.cssHas ) { + + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Our regular `try-catch` mechanism fails to detect natively-unsupported + // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) + // in browsers that parse the `:has()` argument as a forgiving selector list. + // https://drafts.csswg.org/selectors/#relational now requires the argument + // to be parsed unforgivingly, but browsers have not yet fully adjusted. + rbuggyQSA.push( ":has" ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + + // Support: IE <9 only + // IE doesn't have `contains` on `document` so we need to check for + // `documentElement` presence. + // We need to fall back to `a` when `documentElement` is missing + // as `ownerDocument` of elements within `