exchangemarket/public/index.html
2021-10-13 17:32:30 +05:30

1625 lines
81 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RanchiMall market</title>
<script src="components.js" defer></script>
<link rel="stylesheet" href="css/main.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet">
<script id="floGlobals">
/* Constants for FLO blockchain operations !!Make sure to add this at beginning!! */
const floGlobals = {
//Required for all
blockchain: "FLO",
//Required for blockchain API operators
apiURL: {
FLO: ['https://livenet.flocha.in/', 'https://flosight.duckdns.org/'],
FLO_TEST: ['https://testnet-flosight.duckdns.org/', 'https://testnet.flocha.in/']
},
tokenURL: "https://ranchimallflo.duckdns.org/",
token: "rupee",
adminID: "FKAEdnPfjXLHSYwrXQu377ugN4tXU7VGdf",
sendAmt: 0.001,
fee: 0.0005,
}
</script>
<script src="https://sairajzero.github.io/Standard_Operations/cdn/floCrypto.js"></script>
<script src="https://github.com/sairajzero/Standard_Operations/releases/download/test/floBlockchainAPI.js"></script>
<script src="fn.js"></script>
</head>
<body class="hide-completely">
<sm-notifications id="notification_drawer"></sm-notifications>
<audio id="notification_sound">
<source src="https://rmservices.duckdns.org/files/notification-sound.mp3" type="audio/mpeg">
<source src="https://rmservices.duckdns.org/files/notification-sound.ogg" type="audio/ogg">
</audio>
<sm-popup id="confirmation_popup">
<h4 id="confirm_title"></h4>
<p id="confirm_message"></p>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
<sm-button variant="no-outline" class="submit-btn">OK</sm-button>
</div>
</sm-popup>
<sm-popup id="prompt_popup">
<h4 id="prompt_title"></h4>
<p id="prompt_message"></p>
<sm-input id="prompt_input"></sm-input>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
<sm-button variant="no-outline" class="submit-btn" type="submit">OK</sm-button>
</div>
</sm-popup>
<article id="landing" class="page page-layout hide-completely">
<header class="flex space-between">
<div class="logo">
<svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z" />
</svg>
<div class="grid">
<h4>RanchiMall market</h4>
</div>
</div>
<div class="flex">
<!-- <a href="#/sign_up" class="button">Sign up</a> -->
<a href="#/sign_in" class="button button--primary">Sign in</a>
</div>
</header>
<div class="grid justify-center">
<h1 class="h1">Decentralized banking <br> made simple</h1>
<p>*Interest rates are re-calculated with each new transaction done on network</p>
<div class="interest-container">
</div>
</div>
</article>
<article id="sign_in" class="page page-layout hide-completely">
<header>
<div class="logo">
<svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z" />
</svg>
<div class="grid">
<h4>RanchiMall market</h4>
</div>
</div>
</header>
<section>
<h1 class="h2">Sign In</h1>
<p>Welcome back, glad to see you again</p>
<sm-form>
<sm-input id="private_key_field" type="password" placeholder="FLO private key"
error-text="Private key is invalid" data-private-key required></sm-input>
<sm-button id="sign_in_button" variant="primary" disabled>Sign In</sm-button>
</sm-form>
<p>
New here? <a href="#/sign_up">get your FLO login credentials</a>
</p>
</section>
</article>
<article id="sign_up" class="page page-layout hide-completely">
<header>
<div class="logo">
<svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z" />
</svg>
<div class="grid">
<h4>RanchiMall market</h4>
</div>
</div>
</header>
<section class="grid">
<h1 class="h2">FLO credentials</h1>
<p>Get your FLO credentials to use RanchiMall market and all RanchiMall FLO apps. </p>
<div class="grid gap-1-5 card">
<div class="grid gap-0-5">
<h5>FLO ID</h5>
<sm-copy id="generated_flo_id"></sm-copy>
</div>
<div class="grid gap-0-5">
<h5>Private key</h5>
<sm-copy id="generated_private_key"></sm-copy>
</div>
</div>
<sm-button id="sign_up_button" variant="primary">Sign in with these credentials</sm-button>
<strong class="warning">
Keep your private key secure and don't share with anyone.
Once lost there is no way to recover private key.
</strong>
</section>
</article>
<article id="loading" class="page page-layout hide-completely">
<sm-spinner></sm-spinner>
<h4>Loading RanchiMall Market</h4>
</article>
<article id="home" class="page">
<section class="card">
<header id="main_header">
<div class="logo">
<svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z" />
</svg>
<div class="grid">
<h4>RanchiMall Market</h4>
</div>
</div>
<theme-toggle></theme-toggle>
</header>
<sm-form id="login_form" class="hide-">
<div class="grid gap-0-5">
<h4>Login</h4>
<p>Please login for trading and accessing your account.</p>
</div>
<sm-input type="password" id="login_form__priv_key" placeholder="Private key" variant="outlined" animate
required hiderequired></sm-input>
<sm-checkbox id="remember_me" checked>
<span class="button__icon--right">
Remember me
</span>
</sm-checkbox>
<input type="text" id="sign_in_id" style="display: none;" hidden />
<sm-button variant="primary" onclick="UI_evt.login();">Log in</sm-button>
<sm-button onclick="UI_evt.signup();">Not registered? click here!</sm-button>
</sm-form>
<sm-form id="trade_form" class="user-content hide-completely">
<div class="flex align-center space-between">
<p>FLO/INR rate</p>
<span id="flo_rate"></span>
</div>
<div class="flex space-between align-center">
<h4>Trade FLO</h4>
<strip-select id="trade_type_selector" class="tab">
<strip-option value="buy" selected>Buy</strip-option>
<strip-option value="sell">Sell</strip-option>
</strip-select>
</div>
<sm-input id="get_price" variant="outlined" placeholder="Max price (₹)" type="number" step="0.00001"
required hiderequired animate>
</sm-input>
<sm-input id="get_quantity" variant="outlined" placeholder="Quantity (FLO)" type="number"
step="0.00000001" required hiderequired animate></sm-input>
<sm-input id="get_total" variant="outlined" placeholder="Total (₹)" type="number" min="1" step="0.01"
required hiderequired animate>
</sm-input>
<div id="quantity_selector" class="flex align-center">
<span id="quantity_type">Rupee</span>
<button class="button" value="25">25%</button>
<button class="button" value="50">50%</button>
<button class="button" value="75">75%</button>
<button class="button" value="100">100%</button>
</div>
<div id="trade_button_wrapper" class="stateful-button-wrapper flex align-center justify-center">
<sm-button id="trade_button" class="uppercase w-100" variant="primary" onclick="tradeFlo()">BUY FLO
</sm-button>
</div>
</sm-form>
</section>
<section id="orders_section" class="grid gap-1">
<section id="my_orders_section" class="grid gap-1 card user-content hide-completely">
<div id="my_orders_section__header" class="orders_section__header flex">
<div id="orders_section__header--primary" class="flex w-100 align-center space-between">
<h4>
My orders
</h4>
<strip-select id="my_orders_category_selector" class="tab">
<strip-option value="open" selected>Open</strip-option>
<strip-option value="completed">History</strip-option>
</strip-select>
</div>
<div id="orders_section__header--secondary"
class="flex w-100 align-center space-between hide-completely">
<button class="" onclick="clearSelection()" title="Clear all selection">
<svg xmlns="http://www.w3.org/2000/svg" class="icon button__icon--left" height="24px"
viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
<span id="selected_orders"></span>
</button>
<div class="options">
<button class="button" title="Cancel selected orders" onclick="cancelAll()">
Cancel orders
</button>
</div>
</div>
</div>
<ul id="orders_list"></ul>
</section>
<section id="market_orders_section" class="grid gap-1 card">
<div class="orders_section__header flex space-between align-center">
<h4>
Market orders
</h4>
<strip-select id="market_orders_category_selector" class="tab">
<strip-option value="open" selected>Open</strip-option>
<strip-option value="completed">History</strip-option>
</strip-select>
</div>
<ul id="market_orders_list"></ul>
</section>
</section>
<section id="user_section" class="grid card user-content hide-completely">
<h4 class="flex align-center user_section__header">
<svg xmlns="http://www.w3.org/2000/svg" class="icon button__icon--left" height="24px"
viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M21 7.28V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-2.28c.59-.35 1-.98 1-1.72V9c0-.74-.41-1.37-1-1.72zM20 9v6h-7V9h7zM5 19V5h14v2h-6c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h6v2H5z" />
<circle cx="16" cy="12" r="1.5" />
</svg>
My wallet
</h4>
<div id="wallet_actions">
<p class="label">Select asset</p>
<sm-select id="wallet_asset_selector">
<sm-option value="FLO">FLO</sm-option>
<sm-option value="Rupee">Rupee</sm-option>
</sm-select>
<div class="flex wallet_actions__wrapper">
<button class="button" value="deposit">
Deposit
</button>
<button class="button" value="withdraw">
Withdraw
</button>
</div>
</div>
<div class="grid gap-0-5">
<h4>Balance</h4>
<div class="balance-card">
<div class="balance-card__icon">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M16.36,15.39c1.83,0,4.26-2.49,4.36-4.74-5.65-.19-4.91.47-7.28,2.39,2.19-2.4,1.42-7.79-1.43-10V6.17c2.33,1.49,2.21,5.14,0,7.15-2.23-2-2.27-5.69,0-7.15V3c-2.83,2.26-3.62,7.66-1.44,10-2.36-1.93-1.63-2.58-7.28-2.39.1,2.26,2.55,4.73,4.36,4.74,0,0-1.93.22-2.74-2.62,2.38-.37,4.29-.14,6.28,2-.79-.11-4.89,1.13-4.38,3.26.53.06,3,.3,3.58-.83-.17.18-1.25.5-1.53.05.38-1.39,2.32-2,2.32-2-1,1.82-.48,4.63.82,5.72,1.31-1.08,1.8-3.95.82-5.72,0,0,1.95.6,2.32,2-.29.46-1.36.12-1.53-.05.58,1.14,3.06.88,3.58.83.49-2.17-3.58-3.36-4.38-3.26,2-2.17,3.92-2.39,6.28-2C18.3,15.62,16.36,15.39,16.36,15.39ZM12,19.46c-.91-.79-.5-3,0-3.59C12.5,16.45,12.91,18.66,12,19.46Z" />
</svg>
</div>
<div class="balance-card__token">FLO</div>
<div id="flo_balance"></div>
</div>
<div class="balance-card">
<div class="balance-card__icon">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M15.3,4.91a4.78,4.78,0,0,0-.39-.5l3.14,0L18.75,2H6L5.25,4.6H9a4.22,4.22,0,0,1,3.06,1,3.16,3.16,0,0,1,.75,1.24H5.93L5.25,9.22h7.62a3.15,3.15,0,0,1-.34.82,3,3,0,0,1-1.37,1.17,5.34,5.34,0,0,1-2.2.4H5.5l0,1.9,7,8.49h3.56v-.17L9.68,14l.09,0a8.07,8.07,0,0,0,3.65-1,5,5,0,0,0,2-2.09A6.29,6.29,0,0,0,16,9.22h2.1l.69-2.42H15.93A5.93,5.93,0,0,0,15.3,4.91Z" />
</svg>
</div>
<div class="balance-card__token">Rupee</div>
<div id="rupee_balance"></div>
</div>
</div>
</section>
</article>
<sm-popup id="wallet_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="hidePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h4 id="wallet_popup__title" class="capitalize"></h4>
</header>
<sm-form id="wallet_form">
<sm-input id="get_user_amount" variant="outlined" placeholder="Quantity" type="number" step="0.00000001"
required hiderequired animate>
</sm-input>
<sm-input id="get_private_key" variant="outlined" placeholder="FLO private key" type="password" required
error-text="Invalid private key" hiderequired animate>
</sm-input>
<div id="wallet_popup__cta_wrapper" class="stateful-button-wrapper">
<sm-button id="wallet_popup__cta" class="uppercase" variant="primary"></sm-button>
</div>
</sm-form>
</sm-popup>
<sm-popup id="transaction_info_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="hidePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h4>Transaction details</h4>
</header>
<div class="grid gap-1-5">
<div class="grid">
<h5 class="label capitalize">Buyer</h5>
<sm-copy id="transaction__buyer"></sm-copy>
</div>
<div class="grid">
<h5 class="label capitalize">Seller</h5>
<sm-copy id="transaction__seller"></sm-copy>
</div>
<div class="grid">
<h5 class="label">Completed</h5>
<h4 id="transaction_time"></h4>
</div>
</div>
</sm-popup>
<div id="user-container">
<fieldset>
<legend>Profile</legend>
<span id="user_id"></span><br />
<button onclick="proxy.lock();">Add password lock</button><br />
<button onclick="UI_evt.logout();">logout</button>
</fieldset>
</div>
<button onclick="refresh();">Refresh</button>
<template id="net_balance_template">
<span class="available-balance"></span>
</template>
<template id="locked_balance_template">
<div>
<span class="label">Locked</span>
<span class="locked-balance"></span>
</div>
<div>
<span class="label">Available</span>
<span class="available-balance"></span>
</div>
</template>
<template id="order_template">
<li class="list__item order-card">
<sm-checkbox></sm-checkbox>
<div class="grid">
<div class="order-card__type capitalize"></div>
<div class="order-card__quantity"></div>
</div>
<div class="grid">
<span class="label order-card__price-type">Unit price</span>
<div class="order-card__price"></div>
</div>
<time class="order-card__time"></time>
<button class="cancel-order" title="Cancel this order">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z" />
</svg>
<span>Cancel</span>
</button>
</li>
</template>
<template id="market_order_template">
<div class="market-order-card">
<div class="order-card__quantity"></div>
<div class="order-card__price"></div>
</div>
</template>
<template id="transaction_template">
<li class="list__item transaction-card align-center">
<div class="grid">
<div class="transaction-card__type capitalize"></div>
<div class="transaction-card__quantity"></div>
</div>
<div class="grid">
<span class="label transaction-card__price-type">Unit price</span>
<div class="transaction-card__price"></div>
</div>
<div class="grid">
<span class="label transaction-card__price-type">Total</span>
<div class="transaction-card__total"></div>
</div>
<button class="more-info" title="View transaction details">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
</button>
</li>
</template>
<template id="market_transaction_template">
<li class="list__item transaction-card align-center">
<div class="transaction-card__quantity"></div>
<div class="transaction-card__price"></div>
<div class="transaction-card__total"></div>
<button class="more-info" title="View transaction details">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
</button>
</li>
</template>
<template id="market_order_list_template">
<h4 class="grid list__header">
<div>Quantity</div>
<div>Buy price</div>
<div>Sell price</div>
<div>Quantity</div>
</h4>
<section id="buyers_list"></section>
<section id="sellers_list"></section>
</template>
<template id="market_transaction_list_template">
<h4 class="grid list__header">
<div>Quantity</div>
<div>Unit price</div>
<div>Total</div>
<div></div>
</h4>
</template>
<template id="success_template">
<div class="stateful-result stateful-result--success">
<div class="result__background result__background--success"></div>
<div class="icon-wrapper flex align-center">
<svg xmlns="http://www.w3.org/2000/svg" class="icon button__icon--left" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
</svg>
<span>Success</span>
</div>
</div>
</template>
<template id="failure_template">
<div class="stateful-result stateful-result--failure">
<div class="result__background"></div>
<div class="icon-wrapper flex align-center">
<svg xmlns="http://www.w3.org/2000/svg" class="icon button__icon--left" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<path
d="M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
</svg>
<span>Failed</span>
</div>
</div>
</template>
<script id="ui_utils">
// Global variables
const domRefs = {};
let timerId;
const currentYear = new Date().getFullYear();
//Checks for internet connection status
if (!navigator.onLine)
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
{ sound: true }
);
window.addEventListener("offline", () => {
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
{ pinned: true, sound: true }
);
});
window.addEventListener("online", () => {
getRef("notification_drawer").clearAll();
notify("We are back online.", "success");
});
// Use instead of document.getElementById
function getRef(elementId) {
if (!domRefs.hasOwnProperty(elementId)) {
domRefs[elementId] = {
count: 1,
ref: null,
};
return document.getElementById(elementId);
} else {
if (domRefs[elementId].count < 3) {
domRefs[elementId].count = domRefs[elementId].count + 1;
return document.getElementById(elementId);
} else {
if (!domRefs[elementId].ref)
domRefs[elementId].ref = document.getElementById(elementId);
return domRefs[elementId].ref;
}
}
}
// returns dom with specified element
function createElement(tagName, options = {}) {
const { className, textContent, innerHTML, attributes = {} } = options
const elem = document.createElement(tagName)
for (let attribute in attributes) {
elem.setAttribute(attribute, attributes[attribute])
}
if (className)
elem.className = className
if (textContent)
elem.textContent = textContent
if (innerHTML)
elem.innerHTML = innerHTML
return elem
}
// Use when a function needs to be executed after user finishes changes
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
// Limits the rate of function execution
function throttle(func, delay) {
// If setTimeout is already scheduled, no need to do anything
if (timerId) {
return;
}
// Schedule a setTimeout after delay seconds
timerId = setTimeout(function () {
func();
// Once setTimeout function execution is finished, timerId = undefined so that in
// the next scroll event function execution can be scheduled by the setTimeout
timerId = undefined;
}, delay);
}
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
if (this.items.length == 0)
return "Underflow";
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
}
let popupStack = new Stack()
let zIndex = 10
// function required for popups or modals to appear
function showPopup(popupId, pinned) {
zIndex++
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
popupStack = getRef(popupId).show({ pinned, popupStack })
return getRef(popupId);
}
// hides the popup or modal
function hidePopup() {
if (popupStack.peek() === undefined)
return;
popupStack.peek().popup.hide()
}
// displays a popup for asking permission. Use this instead of JS confirm
const getConfirmation = (title, message, cancelText = 'Cancel', confirmText = 'OK') => {
return new Promise(resolve => {
showPopup('confirmation_popup', true)
getRef('confirm_title').textContent = title;
getRef('confirm_message').textContent = message;
let cancelButton = getRef('confirmation_popup').children[2].children[0],
submitButton = getRef('confirmation_popup').children[2].children[1]
submitButton.textContent = confirmText
cancelButton.textContent = cancelText
submitButton.onclick = () => {
hidePopup()
resolve(true);
}
cancelButton.onclick = () => {
hidePopup()
resolve(false);
}
})
}
// displays a popup for asking user input. Use this instead of JS prompt
async function getPromptInput(title, message = '', options = {}) {
const { isPassword = true, cancelText = 'Cancel', confirmText = 'OK' } = options
showPopup('prompt_popup', true)
getRef('prompt_title').textContent = title;
getRef('prompt_message').textContent = message;
let buttons = getRef('prompt_popup').querySelectorAll("sm-button");
if (isPassword)
getRef('prompt_input').setAttribute("type", "password")
getRef('prompt_input').focusIn()
buttons[0].textContent = cancelText;
buttons[1].textContent = confirmText;
return new Promise((resolve, reject) => {
buttons[0].onclick = () => {
hidePopup()
return;
}
buttons[1].onclick = () => {
const value = getRef('prompt_input').value;
hidePopup()
resolve(value)
}
})
}
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
function notify(message, mode, options = {}) {
const { pinned = false, sound = false } = options
let icon
switch (mode) {
case 'success':
icon = `<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`
break;
case 'error':
icon = `<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
break;
}
getRef("notification_drawer").push(message, { pinned, icon });
if (navigator.onLine && sound) {
getRef("notification_sound").currentTime = 0;
getRef("notification_sound").play();
}
if (mode === 'error') {
console.error(message)
}
}
function getFormattedTime(time, relative) {
try {
if (String(time).indexOf('_'))
time = String(time).split('_')[0]
const intTime = parseInt(time)
if (String(intTime).length < 13)
time *= 1000
let timeFrag = new Date(intTime).toString().split(' '),
day = timeFrag[0],
month = timeFrag[1],
date = timeFrag[2],
year = timeFrag[3],
minutes = new Date(intTime).getMinutes(),
hours = new Date(intTime).getHours(),
currentTime = new Date().toString().split(' ')
minutes = minutes < 10 ? `0${minutes}` : minutes
let finalHours = ``;
if (hours > 12)
finalHours = `${hours - 12}:${minutes}`
else if (hours === 0)
finalHours = `12:${minutes}`
else
finalHours = `${hours}:${minutes}`
finalHours = hours >= 12 ? `${finalHours} PM` : `${finalHours} AM`
if (relative) {
if (year == currentYear) {
if (currentTime[1] === month) {
const dateDiff = (parseInt(currentTime[2]) - parseInt(date))
if (dateDiff === 0)
return `${finalHours}`;
else if (dateDiff === 1)
return `Yesterday`;
else if (dateDiff > 1 && dateDiff < 8)
return currentTime[0];
else
return ` ${date} ${month}`;
}
else
return ` ${date} ${month}`;
}
else
return `${month} ${year}`;
}
else
return `${finalHours}, ${month} ${date} ${year}`;
} catch (e) {
console.error(e);
return time;
}
}
window.addEventListener('hashchange', e => showPage(window.location.hash))
window.addEventListener("load", () => {
document.body.classList.remove('hide-completely')
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
document.addEventListener('keyup', (e) => {
if (e.code === 'Escape') {
hidePopup()
}
})
document.addEventListener('copy', () => {
notify('copied', 'success')
})
});
const pagesData = {
openedPages: [],
params: {}
}
async function showPage(targetPage, options = {}) {
const { firstLoad, hashChange } = options
let pageId
let subPageId
let searchParams
let params
if (targetPage === '') {
if (typeof myFloID === "undefined") {
pageId = 'landing'
} else {
pageId = 'home'
}
} else {
if (targetPage.includes('/')) {
if (targetPage.includes('?')) {
const splitAddress = targetPage.split('?')
searchParams = splitAddress.pop()
const pages = splitAddress.pop().split('/')
pageId = pages[1]
subPageId = pages[2]
} else {
const pages = targetPage.split('/')
pageId = pages[1]
subPageId = pages[2]
}
} else {
pageId = targetPage
}
}
if (typeof myFloID === "undefined" && !(['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) return
if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams);
params = Object.fromEntries(urlSearchParams.entries());
pagesData.params = params
}
if (pagesData.lastPage !== pageId) {
switch (pageId) {
case 'sign_in':
setTimeout(() => {
getRef('private_key_field').focusIn()
}, 0);
break;
case 'sign_up':
const { floID, privKey } = floCrypto.generateNewID()
getRef('generated_flo_id').value = floID
getRef('generated_private_key').value = privKey
break;
}
document.querySelector('.page:not(.hide-completely)')?.classList.add('hide-completely')
getRef(pageId)?.classList.remove('hide-completely')
getRef(pageId)?.animate([
{
opacity: 0,
},
{
opacity: 1,
},
],
{
duration: 300,
easing: 'ease'
})
}
pagesData.lastPage = pageId
if (!pagesData.openedPages.includes(pageId)) {
pagesData.openedPages.push(pageId)
}
}
// class based lazy loading
class LazyLoader {
constructor(container, elementsToRender, renderFn, options = {}) {
const { batchSize = 10 } = options
this.elementsToRender = elementsToRender
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
this.renderFn = renderFn
this.intersectionObserver
this.batchSize = batchSize
this.lazyContainer = document.querySelector(container)
this.update = this.update.bind(this)
this.render = this.render.bind(this)
this.init = this.init.bind(this)
this.clear = this.clear.bind(this)
}
init() {
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
observer.disconnect()
this.render({ lazyLoad: true })
}
})
}, {
threshold: 0.3
})
this.mutationObserver = new MutationObserver(mutationList => {
mutationList.forEach(mutation => {
if (mutation.type === 'childList') {
if (mutation.addedNodes.length) {
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
}
}
})
})
this.mutationObserver.observe(this.lazyContainer, {
childList: true,
})
this.render()
}
update(elementsToRender) {
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
this.render()
}
render(options = {}) {
let { lazyLoad = false } = options
const frag = document.createDocumentFragment();
if (lazyLoad) {
this.updateStartIndex = this.updateEndIndex
this.updateEndIndex = this.arrayOfElements.length > this.updateEndIndex + this.batchSize ? this.updateEndIndex + this.batchSize : this.arrayOfElements.length
} else {
this.intersectionObserver.disconnect()
this.lazyContainer.innerHTML = ``;
this.updateStartIndex = 0
this.updateEndIndex = this.arrayOfElements.length > this.batchSize ? this.batchSize : this.arrayOfElements.length
}
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
frag.append(this.renderFn(this.arrayOfElements[index]))
}
this.lazyContainer.append(frag)
}
clear() {
this.intersectionObserver.disconnect()
this.mutationObserver.disconnect()
this.lazyContainer.innerHTML = ``;
}
reset() {
this.arrayOfElements = (typeof this.elementsToRender === 'function') ? this.elementsToRender() : this.elementsToRender || []
this.render()
}
}
</script>
<script>
let user_id; //container for user ID and proxy private-key
const proxy = {
private: null,
public: null,
async lock() {
if (!this.private)
throw "No proxy key found!";
let pwd = await getPromptInput("Enter password", '', { isPassword: true });
if (!pwd)
notify("Password cannot be empty", 'error');
else if (pwd.length < 4)
notify("Password minimum length is 4", 'error');
else {
let tmp = Crypto.AES.encrypt(this.private, pwd);
localStorage.setItem("proxy_secret", "?" + tmp);
notify("Successfully locked with Password", 'success');
}
},
clear() {
localStorage.removeItem("proxy_secret");
this.private = null;
this.public = null;
},
set secret(key) {
localStorage.setItem("proxy_secret", key);
this.private = key;
this.public = floCrypto.getPubKeyHex(key);
},
get secret() {
if (this.private)
return this.private;
try {
let tmp = localStorage.getItem("proxy_secret");
if (typeof tmp === "string" && tmp.startsWith("?")) {
getPromptInput("Enter password", '', { isPassword: true }).then(pwd => {
if (!pwd)
throw "Password Required for making transactions";
else {
try {
tmp = Crypto.AES.decrypt(tmp.substring(1), pwd);
} catch (error) {
throw "Incorrect Password! Password Required for making transactions";
}
}
});
}
this.private = tmp;
this.public = floCrypto.getPubKeyHex(tmp);
return this.private;
} catch (error) {
alert(error);
console.error(error);
throw "Unable to fetch Proxy secret";
}
}
}
function updateRate() {
getRate().then(rate => {
pe.flo = rate
getRef('flo_rate').textContent = formatAmount(parseFloat(rate))
}).catch(error => console.error(error))
}
function refresh(init = false) {
if (init)
console.info("init");
else
console.info("refresh");
updateRate()
account();
}
function showBalance(containerId, availableBalance = 0, lockedBalance = 0) {
getRef(containerId).innerHTML = ''
const templateToClone = lockedBalance ? 'locked_balance_template' : 'net_balance_template';
const card = getRef(templateToClone).content.cloneNode(true).firstElementChild
card.querySelector('.available-balance').textContent = availableBalance
if (lockedBalance) {
card.querySelector('.locked-balance').textContent = lockedBalance
}
getRef(containerId).className = lockedBalance ? 'grid balance-card__amount-wrapper' : ''
getRef(containerId).parentNode.className = `balance-card ${lockedBalance ? 'is-locked' : ''}`
getRef(containerId).append(card)
}
const myBuyOrders = [
{
id: 'dfs5g16sdg1',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.36,
},
{
id: 'f4gd1d56fg1',
time_placed: generateRandomDate(),
quantity: 78.3,
maxPrice: 1.26,
},
{
id: 's4dg5s4d1g98',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.28,
},
]
const mySellOrders = [
{
id: 'sd8g45g419s6',
time_placed: generateRandomDate(),
quantity: 15,
minPrice: 1.16,
},
{
id: 's59d1g9ws18d',
time_placed: generateRandomDate(),
quantity: 8.3,
minPrice: 1.86,
},
{
id: 'w899e1g4d1g98',
time_placed: generateRandomDate(),
quantity: 18.5,
minPrice: 1.64,
},
]
const myTransactions = [
{
id: 'sd8g45g419s6',
tx_time: generateRandomDate(),
quantity: 15,
unitValue: 1.16,
buyer: 'w8eg4w8eg4w98werg9'
},
{
id: 's59d1g9ws18d',
tx_time: generateRandomDate(),
quantity: 8.3,
unitValue: 1.86,
buyer: 'w8eg4w8eg4w98werg9'
},
{
id: 'w899e1g4d1g98',
tx_time: generateRandomDate(),
quantity: 18.5,
unitValue: 1.64,
buyer: 'w8eg4w8eg4w98werg9'
},
]
const market = {
buyOrders: [
{
id: 'dfs5g16sdg1',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.36,
},
{
id: 'f4gd1d56fg1',
time_placed: generateRandomDate(),
quantity: 78.3,
maxPrice: 1.26,
},
{
id: 's4dg5s4d1g98',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.28,
},
],
sellOrders: [
{
id: 'sd8g45g419s6',
time_placed: generateRandomDate(),
quantity: 15,
minPrice: 1.16,
},
{
id: 's59d1g9ws18d',
time_placed: generateRandomDate(),
quantity: 8.3,
minPrice: 1.86,
},
{
id: 'w899e1g4d1g98',
time_placed: generateRandomDate(),
quantity: 18.5,
minPrice: 1.64,
},
]
}
function generateRandomDate() {
return new Date() - Math.floor(Math.random() * 10000000000);
}
let accountDetails = {}
function account() {
getAccount().then(acc => {
accountDetails = acc
console.debug(acc);
//Element display
document.querySelectorAll(".user-content").forEach(elem => elem.classList.remove('hide-completely'))
getRef('trade_form').classList.remove('hide-completely')
getRef("user_id").textContent = acc.floID;
user_id = acc.floID;
//FLO Balance
let flo_total = acc.coins.reduce((a, x) => a + x.quantity, 0);
let flo_locked = acc.sellOrders.reduce((a, x) => a + x.quantity, 0);
let flo_net = flo_total - flo_locked;
console.debug("FLO", flo_total, flo_locked, flo_net);
showBalance("flo_balance", flo_net, flo_locked)
//Rupee Balance
let rupee_total = acc.rupee_total;
let rupee_locked = acc.buyOrders.reduce((a, x) => a + x.quantity * x.maxPrice, 0);
let rupee_net = rupee_total - rupee_locked;
console.debug("RUPEE", rupee_total, rupee_locked, rupee_net);
showBalance("rupee_balance", rupee_net, rupee_locked)
//My orders
renderUserOrders()
try {
proxy.secret;
} catch (error) {
console.warn(error);
}
}).catch(error => {
if (error instanceof ResponseError) {
let response = JSON.parse(error.data)
console.log(error);
console.log(response);
getRef("login_form").classList.remove('hide-completely')
document.querySelectorAll(".user-content").forEach(elem => elem.classList.add('hide-completely'))
getRef('sign_in_id').value = response.sid;
proxy.clear();
} else
console.error(error);
})
};
const UI_evt = {};
UI_evt.signup = async function () {
let sid = getRef('sign_in_id').value;
let privKey = await getPromptInput("Register private key", "Enter the private key of floID you want to register",
{
isPassword: true,
confirmText: 'Register'
}
);
if (privKey) {
signUp(privKey, sid).then(result => {
console.info(result);
notify("Account registered!", 'success')
}).catch(error => {
notify(error, 'error');
});
}
};
UI_evt.logout = function () {
logout().then(result => {
console.warn(result);
proxy.clear();
location.reload();
}).catch(error => console.error(error));
};
UI_evt.login = function () {
let privKey = getRef('login_form__priv_key').value;
let sid = getRef('sign_in_id').value;
let rememberMe = getRef('remember_me').checked;
let tmpKey = floCrypto.generateNewID();
login(privKey, tmpKey.pubKey, sid, rememberMe).then(result => {
console.log(result);
proxy.secret = tmpKey.privKey;
account();
}).catch(error => console.error(error));
};
UI_evt.cancelOrders = function () {
let container = document.getElementById('my-orders');
let cancel = [];
let inputs = container.querySelectorAll('input[checked]')
for (let i = 0; i < inputs.length; i++) {
let row = inputs[i].parentElement.parentElement
let id = row.dataset['id'];
let type = row.parentElement.dataset['type'];
cancel.push([type, id]);
}
cancel.forEach(o => cancelOrder(o[0], o[1], proxy.secret)
.then(result => console.log(result))
.catch(error => console.error(o, error)))
};
refresh(true);
</script>
<script>
function formatAmount(amount) {
return amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR' })
}
const render = {
orderCard(orderDetails = {}) {
const { id, quantity, price, time, type } = orderDetails
const card = getRef('order_template').content.cloneNode(true).firstElementChild
card.dataset.id = id
card.dataset.type = type
card.querySelector('.order-card__type').textContent = type
card.querySelector('.order-card__quantity').textContent = `${quantity} FLO`
card.querySelector('.order-card__price-type').textContent = type === 'buy' ? 'Max price' : 'Min price'
card.querySelector('.order-card__price').textContent = formatAmount(price)
card.querySelector('.order-card__time').textContent = getFormattedTime(time, true)
return card
},
transactionCard(transactionDetails = {}) {
const { buyer, seller, type, other, quantity, unitValue, time } = transactionDetails
const card = getRef('transaction_template').content.cloneNode(true).firstElementChild
card.dataset.type = type
card.querySelector('.transaction-card__type').textContent = type
card.querySelector('.transaction-card__quantity').textContent = `${quantity} FLO`
card.querySelector('.transaction-card__price').textContent = `${unitValue}`
card.querySelector('.transaction-card__total').textContent = formatAmount(parseFloat((unitValue * quantity).toFixed(2)))
card.querySelector('.more-info').dataset.buyer = buyer
card.querySelector('.more-info').dataset.seller = seller
card.querySelector('.more-info').dataset.other = other
card.querySelector('.more-info').dataset.time = time
return card
},
marketOrderCard(orderDetails = {}) {
const { id, quantity, price, time, type } = orderDetails
const card = getRef('market_order_template').content.cloneNode(true).firstElementChild
card.dataset.id = id
card.dataset.type = type
card.classList.add(`market-order-card--${type}`)
card.querySelector('.order-card__quantity').textContent = `${quantity} FLO`
card.querySelector('.order-card__price').textContent = formatAmount(price)
return card
},
marketTransactionCard(transactionDetails = {}) {
const { buyer, seller, quantity, unitValue, time } = transactionDetails
const card = getRef('market_transaction_template').content.cloneNode(true).firstElementChild
card.querySelector('.transaction-card__quantity').textContent = `${quantity} FLO`
card.querySelector('.transaction-card__price').textContent = `${unitValue}`
card.querySelector('.transaction-card__total').textContent = formatAmount(parseFloat((unitValue * quantity).toFixed(2)))
card.querySelector('.more-info').dataset.time = time
card.querySelector('.more-info').dataset.buyer = buyer
card.querySelector('.more-info').dataset.seller = seller
return card
},
}
function showProcess(id) {
getRef(id).children[0].classList.add('clip')
getRef(id).append(document.createElement('sm-spinner'))
}
function hideProcess(id) {
getRef(id).children[0].classList.remove('clip')
getRef(id).querySelector('sm-spinner')?.remove()
}
let tradeType = 'buy'
getRef('trade_type_selector').addEventListener('change', e => {
tradeType = e.detail.value
getRef('get_price').setAttribute('placeholder', tradeType === 'buy' ? 'Max price' : 'Min price')
getRef('trade_button').textContent = `${tradeType} FLO`
getRef('quantity_type').textContent = tradeType === 'buy' ? `Rupee` : `FLO`
})
async function tradeFlo() {
const quantity = parseFloat(getRef('get_quantity').value)
const price = parseFloat(getRef('get_price').value)
showProcess('trade_button_wrapper')
try {
if (tradeType === 'buy') {
await buy(quantity, price, proxy.secret)
} else {
await sell(quantity, price, proxy.secret)
}
getRef('trade_button_wrapper').append(getRef('success_template').content.cloneNode(true))
notify(`Placed ${tradeType} order`, 'success')
}
catch (err) {
getRef('trade_button_wrapper').append(getRef('failure_template').content.cloneNode(true))
notify(err, 'error')
}
finally {
getRef('trade_form').reset()
setTimeout(() => {
hideProcess('trade_button_wrapper')
setTimeout(() => {
getRef('trade_button_wrapper').querySelector('.stateful-result').remove()
}, 100);
}, 4000);
}
}
const balance = {
flo: 5.1245,
rupee: 457.2
}
const rate = {
flo: 1.487
}
getRef('quantity_selector').addEventListener('click', e => {
// Get latest balance and exchange rate
if (e.target.closest('button')) {
const target = e.target.closest('button')
const unitPrice = parseFloat(getRef('get_price').value)
const fraction = parseInt(target.value) / 100
if (tradeType === 'buy') {
getRef('get_total').value = parseFloat((fraction * balance.rupee).toFixed(2))
getRef('get_quantity').value = parseFloat(((balance.rupee * fraction) / unitPrice).toFixed(5))
} else {
getRef('get_total').value = parseFloat(((fraction * balance.flo) * rate.flo).toFixed(2))
getRef('get_quantity').value = parseFloat((balance.flo * fraction).toFixed(5))
}
}
})
getRef('get_price').addEventListener('keyup', e => {
const unitPrice = parseFloat(getRef('get_price').value) || 0
const quantity = parseFloat(getRef('get_quantity').value) || 0
getRef('get_total').value = parseFloat((quantity * unitPrice).toFixed(2)) || 0
})
getRef('get_quantity').addEventListener('keyup', e => {
const unitPrice = parseFloat(getRef('get_price').value)
getRef('get_total').value = parseFloat((parseFloat(e.target.value) * unitPrice).toFixed(2)) || 0
})
getRef('get_total').addEventListener('keyup', e => {
const unitPrice = parseFloat(getRef('get_price').value)
getRef('get_quantity').value = parseFloat((parseFloat(e.target.value) / unitPrice).toFixed(5)) || 0
})
getRef('wallet_actions').addEventListener('click', e => {
if (e.target.closest('.button')) {
const target = e.target.closest('.button')
showPopup('wallet_popup')
const type = target.value
const asset = getRef('wallet_asset_selector').value
getRef('wallet_popup__cta').textContent = `${type} ${asset}`
getRef('wallet_popup__cta').setAttribute('value', type)
getRef('wallet_popup__title').textContent = `${type} ${asset}`
if (type === 'withdraw') {
getRef('get_private_key').classList.add('hide-completely')
getRef('get_private_key').removeAttribute('required')
getRef('get_private_key').removeAttribute('hiderequired')
} else {
getRef('get_private_key').setAttribute('required', '')
getRef('get_private_key').setAttribute('hiderequired', '')
getRef('get_private_key').classList.remove('hide-completely')
}
getRef('wallet_form').elementsChanged()
}
})
getRef('wallet_popup__cta').addEventListener('click', async e => {
const asset = getRef('wallet_asset_selector').value
const type = e.target.getAttribute('value')
const quantity = parseFloat(getRef('get_quantity').value)
try {
showProcess('wallet_popup__cta_wrapper')
if (type === 'deposit') {
const privKey = getRef('get_private_key').value;
if (asset === 'FLO') {
await depositFLO(quantity, user_id, privKey, proxy.secret)
} else {
await depositRupee(quantity, user_id, privKey, proxy.secret)
}
notify(`Deposited ${asset} successfully`)
} else {
if (asset === 'FLO') {
await withdrawFLO(quantity, proxy.secret)
} else {
await withdrawRupee(quantity, proxy.secret)
}
notify(`Withdrawn ${asset} successfully`)
}
}
catch (err) {
notify(err, 'error')
}
finally {
hideProcess('wallet_popup__cta_wrapper')
}
})
const selectedOrders = new Map()
const slideInLeft = [
{
opacity: 0,
transform: 'translateX(1rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutLeft = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(-1rem)'
},
]
const slideInRight = [
{
opacity: 0,
transform: 'translateX(-1rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutRight = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(1rem)'
},
]
getRef('orders_list').addEventListener('change', e => {
const animOptions = {
duration: 150,
easing: 'ease',
fill: 'forwards'
}
const target = e.target.parentNode;
target.classList.toggle('order-card--selected')
if (e.target.checked) {
selectedOrders.set(target.dataset.id, target.dataset.type)
} else {
selectedOrders.delete(target.dataset.id)
}
getRef('selected_orders').textContent = `${selectedOrders.size} selected`
if (selectedOrders.size === 1 && !getRef('my_orders_section__header').children[0].classList.contains('hide-completely')) {
getRef('my_orders_section__header').children[0].animate(slideOutLeft, animOptions)
.onfinish = () => {
getRef('my_orders_section__header').children[0].classList.add('hide-completely')
getRef('my_orders_section__header').children[1].classList.remove('hide-completely')
getRef('my_orders_section__header').children[1].animate(slideInLeft, animOptions)
}
} else if (selectedOrders.size === 0 && getRef('my_orders_section__header').children[0].classList.contains('hide-completely')) {
getRef('my_orders_section__header').children[1].animate(slideOutRight, animOptions)
.onfinish = () => {
getRef('my_orders_section__header').children[1].classList.add('hide-completely')
getRef('my_orders_section__header').children[0].classList.remove('hide-completely')
getRef('my_orders_section__header').children[0].animate(slideInRight, animOptions)
}
}
})
function clearSelection() {
getRef('orders_list').querySelectorAll('sm-checkbox[checked]').forEach(elem => elem.checked = false)
}
getRef('orders_list').addEventListener('click', e => {
if (e.target.closest('.cancel-order')) {
getConfirmation('Cancel this order?').then(res => {
if (res) {
const target = e.target.closest('.order-card')
const id = target.dataset.id
const type = target.dataset.type
cancelOrder(type, id, proxy.secret)
.then(() => {
notify('Order cancelled', 'success')
target.remove()
})
.catch(err => notify(err, 'error'))
}
})
} else if (e.target.closest('.more-info')) {
const target = e.target.closest('.more-info')
showPopup('transaction_info_popup')
getRef('transaction__buyer').value = target.dataset.buyer
getRef('transaction__seller').value = target.dataset.seller
getRef('transaction_time').textContent = getFormattedTime(target.dataset.time)
}
})
function cancelAll() {
getConfirmation('Cancel all selected orders?').then(async res => {
if (res) {
try {
await Promise.all(
selectedOrders.map((type, id) => cancelOrder(type, id, proxy.secret))
)
selectedOrders.forEach((type, id) => {
getRef('orders_list').querySelector(`[data-id="${id}"]`).remove()
})
notify('All selected orders cancelled', 'success')
selectedOrders.clear()
}
catch (err) {
notify(err, 'error')
}
}
})
}
function renderUserOrders() {
const { buyOrders, sellOrders, transactions } = accountDetails
getRef('orders_list').innerHTML = '';
const frag = document.createDocumentFragment()
const ordersType = getRef('my_orders_category_selector').value
if (ordersType === 'open') {
const allOpenOrders = [...(buyOrders || myBuyOrders), ...(sellOrders || mySellOrders)].sort((a, b) => b.time_placed - a.time_placed)
allOpenOrders.forEach(order => {
const { id, quantity, minPrice = undefined, maxPrice = undefined, time_placed } = order
const orderDetails = {
id,
quantity,
type: minPrice ? 'sell' : 'buy',
price: minPrice || maxPrice,
time: time_placed
}
frag.append(render.orderCard(orderDetails))
})
} else {
(transactions || myTransactions).forEach(transaction => {
const { floID, quantity, unitValue, tx_time, buyer, seller } = transaction
let type, other;
if (seller === floID) {
type = 'Sold';
other = buyer === floID ? 'MySelf' : buyer;
} else if (buyer === floID) {
type = 'Bought';
other = seller;
} else
return;
const transactionDetails = {
buyer,
seller,
type,
other,
quantity,
unitValue,
time: tx_time
}
frag.append(render.transactionCard(transactionDetails))
});
}
getRef('orders_list').append(frag)
}
window.addEventListener('load', () => {
renderUserOrders()
renderMarketOrders()
})
getRef('my_orders_category_selector').addEventListener('change', renderUserOrders)
getRef('market_orders_category_selector').addEventListener('change', renderMarketOrders)
async function renderMarketOrders() {
getRef('market_orders_list').innerHTML = '';
const ordersType = getRef('market_orders_category_selector').value
getRef('market_orders_list').className = ordersType
if (ordersType === 'open') {
try {
const buyersFrag = document.createDocumentFragment()
const sellersFrag = document.createDocumentFragment()
// const [buyOrders, sellOrders] = await Promise.all([getBuyList(), getSellList()])
const { buyOrders, sellOrders } = market
buyOrders.forEach(order => {
const { id, quantity, maxPrice, time_placed } = order
const orderDetails = {
id,
quantity,
price: maxPrice,
time: time_placed,
type: 'buy'
}
buyersFrag.append(render.marketOrderCard(orderDetails))
});
sellOrders.forEach(order => {
const { id, quantity, minPrice, time_placed } = order
const orderDetails = {
id,
quantity,
price: minPrice,
time: time_placed,
type: 'sell'
}
sellersFrag.append(render.marketOrderCard(orderDetails))
});
const marketOrders = getRef('market_order_list_template').content.cloneNode(true)
marketOrders.querySelector('#buyers_list').append(buyersFrag)
marketOrders.querySelector('#sellers_list').append(sellersFrag)
getRef('market_orders_list').append(marketOrders)
}
catch (err) {
notify(err, 'error')
}
} else {
const frag = document.createDocumentFragment()
try {
// const marketTransactions = await getTransactionList()
const marketTransactions = myTransactions
marketTransactions.forEach(transaction => {
const { seller, buyer, quantity, unitValue, tx_time } = transaction
const transactionDetails = {
buyer,
seller,
quantity,
unitValue,
time: tx_time
}
frag.append(render.marketTransactionCard(transactionDetails))
})
const marketTransactionList = getRef('market_transaction_list_template').content.cloneNode(true)
getRef('market_orders_list').append(marketTransactionList, frag)
}
catch (err) {
notify(err, 'error')
}
}
}
getRef('market_orders_list').addEventListener('click', e => {
if (e.target.closest('.more-info')) {
const target = e.target.closest('.more-info')
showPopup('transaction_info_popup')
getRef('transaction__buyer').value = target.dataset.buyer
getRef('transaction__seller').value = target.dataset.seller
getRef('transaction_time').textContent = getFormattedTime(target.dataset.time)
}
})
</script>
</body>
</html>