UNPKG

securewalletbundle

Version:

Un wallet 100% sécurisé, sécurisé par vous. Pour XRP et XLM pour l'instant. Vous pouvez télécharger et vérifier/valider le code grâce à ce package.

674 lines (662 loc) 28.2 kB
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="en_US"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="author" content="Decrypto"/> <meta name="description" content="Envoyer une transation - ONLINE"/> <meta name="version" content="2.0"/> <title>2.3 - ONLINE - Send Transaction</title> <!-- ////////////////////////////////////////////////////// LIBS PART ////////////////////////////////////////////////////// --> <!-- UI BOOTSTRAP CSS LIBS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous"> <!-- JQUERY LIBS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <!-- SCRIPT FOR XRP & XLM BLOCKCHAIN --> <script src="https://unpkg.com/securewalletbundle@2.5.3/dist/SecureWalletBundle.browser.min.js" integrity="sha256-2ZubSXQsoaoPU0YsTc6BkLBXVfEHjBysA4v/e+HDwHw=" crossorigin="anonymous"></script> <!-- //////////////////////////////////////////////////// END LIBS PART //////////////////////////////////////////////////// --> <!-- BLOCKCHAIN NETWORK MANAGEMENT --> <script> const {xrpl,xrplCodec,StellarSdk} = SecureWalletBundle; const CLASSES = { //------------------------------------- XRPL ------------------------------------------- "XRPL":class XRPL { static serverURL = { "main":"wss://xrplcluster.com", "test":"wss://s.altnet.rippletest.net:51233"}; static isLibLoaded(){ return !(typeof xrpl === "undefined"); } static curMaxLen = 6; symbol = "XRP"; constructor(mt) { this.main = (mt || false) && mt=="main"; this.client = new xrpl.Client(XRPL.serverURL[this.main?"main":"test"]); } isMain(){ return this.main; } async connect(){ if (this.isConnected !== true){ await this.client.connect(); this.isConnected = true; } } switchServer(mt){ if (!mt){ mt = this.main?"test":"main"; } mt = mt=="main"; if (this.main && !mt){//we are main, we go test if (this.isConnected === true){ this.isConnected = false; try { this.client.disconnect(); } catch(err){} } this.client = new xrpl.Client(XRPL.serverURL["test"]); this.main = false; } else if (!this.main && mt){//we are test, we go main if (this.isConnected === true){ this.isConnected = false; try { this.client.disconnect(); } catch(err){} } this.client = new xrpl.Client(XRPL.serverURL["main"]); this.main = true; } } #cur40ToCur(text){ if (text.length < XRPL.curMaxLen){ return text; } return text.match(/.{1,2}/g).map(function(v){ return String.fromCharCode(parseInt(v, 16)); }).join('').replace(/\0/g, '').toUpperCase(); } decode(trx){ var rawTrx = xrplCodec.decode(trx.tx_blob); switch(rawTrx.TransactionType){ case "Payment": return { type : "Payment", trx : { from : rawTrx.Account, to : rawTrx.Destination, currency : rawTrx.Amount.currency?this.#cur40ToCur(rawTrx.Amount.currency):this.symbol, amount : rawTrx.Amount.value?rawTrx.Amount.value:xrpl.dropsToXrp(rawTrx.Amount), fee : xrpl.dropsToXrp(rawTrx.Fee), tag : rawTrx.DestinationTag, multisign : rawTrx.SigningPubKey == "" } }; case "TrustSet": return { type : rawTrx.LimitAmount.value==0?"TrustRev":"TrustSet", trx : { from : rawTrx.Account, issuer : rawTrx.LimitAmount.issuer, currency : this.#cur40ToCur(rawTrx.LimitAmount.currency), max : rawTrx.LimitAmount.value, fee : xrpl.dropsToXrp(rawTrx.Fee), multisign : rawTrx.SigningPubKey == "" } }; case "AccountDelete": return { type : "AccountDelete", trx : { from : rawTrx.Account, to : rawTrx.Destination, fee : xrpl.dropsToXrp(rawTrx.Fee), tag : rawTrx.DestinationTag, multisign : rawTrx.SigningPubKey == "" } }; case "SignerListSet": return { type : rawTrx.SignerQuorum==0?"SignerListDel":"SignerListSet", trx : { from : rawTrx.Account, signer : rawTrx.SignerEntries?rawTrx.SignerEntries[0].SignerEntry.Account:false, fee : xrpl.dropsToXrp(rawTrx.Fee), multisign : rawTrx.SigningPubKey == "" } }; case "AccountSet": var flag = rawTrx.SetFlag || rawTrx.ClearFlag || false; if (flag === false || flag != xrpl.AccountSetAsfFlags.asfDisableMaster){ return { type:"unknown", trx : rawTrx, multisign : rawTrx.SigningPubKey == "" }; } return { type : rawTrx.SetFlag?"MasterOff":"MasterOn", trx : { from : rawTrx.Account, fee : xrpl.dropsToXrp(rawTrx.Fee), multisign : rawTrx.SigningPubKey == "" } }; default: return { type:"unknown", trx : rawTrx, multisign : rawTrx.SigningPubKey == "" }; } } async send(tx){ if (!tx.tx_blob) throw "Cette transaction n'est pas une transaction XRPL !"; this.lastSendSuccess = false; this.lastTrxHash = tx.hash; var resp = await this.client.submitAndWait(tx.tx_blob); this.lastSendSuccess = (resp.result.meta.TransactionResult == "tesSUCCESS") || resp.result.meta.TransactionResult || false; return resp; } getLastSendSuccess(){ return this.lastSendSuccess; } buildTrxLink(){ return (this.main?'https://xrpscan.com/tx/':'https://testnet.xrpl.org/transactions/')+this.lastTrxHash; } tryReadError(err){ return err.toString(); } isSuchTransaction(trx){ return trx.tx_blob && trx.hash ? true : false; } }, //------------------------------------- XLM ------------------------------------------- "XLM":class XLM { static serverURL = { "main":"https://horizon.stellar.org", "test":"https://horizon-testnet.stellar.org"}; static isLibLoaded(){ return !(typeof StellarSdk === "undefined"); } symbol = "XLM"; constructor(mt) { this.main = (mt || false) && mt=="main"; this.server = new StellarSdk.Horizon.Server(XLM.serverURL[this.main?"main":"test"]); } isMain(){ return this.main; } async connect(){ this.isConnected = true; } switchServer(mt){ if (!mt){ mt = this.main?"test":"main"; } mt = mt=="main"; if (this.main && !mt){//we are main, we go test this.server = new StellarSdk.Horizon.Server(XLM.serverURL["test"]); this.main = false; } else if (!this.main && mt){//we are test, we go main this.server = new StellarSdk.Horizon.Server(XLM.serverURL["main"]); this.main = true; } } decode(trx){ var rawTrx = StellarSdk.TransactionBuilder.fromXDR(trx.XDR,trx.Network); switch(rawTrx._operations[0].type){ case "createAccount": case "payment": return { type : "Payment", trx : { from : rawTrx._source, to : rawTrx._operations[0].destination, currency : rawTrx._operations[0].type=="createAccount"?this.symbol:rawTrx._operations[0].asset.code, amount : rawTrx._operations[0].type=="createAccount"?rawTrx._operations[0].startingBalance.replace(/\.?0+$/,''):rawTrx._operations[0].amount.replace(/\.?0+$/,''), fee : (rawTrx._fee/Math.pow(10,7)).toFixed(7).replace(/0+$/,''), tag : rawTrx._memo._value?(new TextDecoder().decode(rawTrx._memo._value)):rawTrx._memo._value } } case "changeTrust": return { type : rawTrx._operations[0].limit==0?"TrustRev":"TrustSet", trx : { from : rawTrx._source, issuer : rawTrx._operations[0].line.issuer, currency : rawTrx._operations[0].line.code, max : rawTrx._operations[0].limit.replace(/\.?0+$/,''), fee : (rawTrx._fee/Math.pow(10,7)).toFixed(7).replace(/0+$/,''), } }; case "accountMerge": return { type : "AccountDelete", trx : { from : rawTrx._source, to : rawTrx._operations[0].destination, fee : (rawTrx._fee/Math.pow(10,7)).toFixed(7).replace(/0+$/,''), tag : rawTrx._memo._value?(new TextDecoder().decode(rawTrx._memo._value)):rawTrx._memo._value, } }; case "setOptions": if (rawTrx._operations[0].signer || rawTrx._operations[0].masterWeight >= 0){ var signer = rawTrx._operations[0].signer; var onMaster = rawTrx._operations[0].masterWeight >= 0; var signSet = onMaster?rawTrx._operations[0].masterWeight>0:signer.weight > 0; return { type : onMaster?(signSet?"MasterOn":"MasterOff"):(signSet?"SignerListSet":"SignerListDel"), trx : { from : rawTrx._source, signer : onMaster?false:signer.ed25519PublicKey, fee : (rawTrx._fee/Math.pow(10,7)).toFixed(7).replace(/0+$/,''), multisign : false } }; } else { return { type:"unknown", trx : rawTrx, multisign : false }; } default: return { type:"unknown", trx : rawTrx, multisign : false }; } } async send(tx){ if (!tx.XDR) throw "Cette transaction n'est pas une transaction XLM !"; this.lastSendSuccess = false; var transaction = StellarSdk.TransactionBuilder.fromXDR(tx.XDR,tx.Network); var transactionResult = await this.server.submitTransaction(transaction); this.lastTrxHash = transactionResult.id; this.lastSendSuccess = true; return transactionResult; } getLastSendSuccess(){ return this.lastSendSuccess; } buildTrxLink(){ return (this.main?'https://lumenscan.io/txns/':'https://testnet.lumenscan.io/txns/')+this.lastTrxHash; } tryReadError(err){ if (err.response && err.response.data && err.response.data.extras && err.response.data.extras.result_codes && err.response.data.extras.result_codes){ return err.toString()+" - Reason : <br/>"+JSON.stringify(err.response.data.extras.result_codes,null, 3); } else { return err.toString(); } } isSuchTransaction(trx){ return trx.XDR && trx.Network ? true : false; } } }; var NETWORK; $(document).on('NETWORK_CHANGED', (e,n,s) => { var _class_ = CLASSES[n]; if (_class_.isLibLoaded()){ NETWORK = new _class_(s); $('#libCompromised').hide(); $('#bg-cur span.cur').text(NETWORK.symbol); $('.container').show(); } else { $('#libCompromised span').text(n); $('#libCompromised').show(); $('.container').hide(); } }); </script> <!-- MAIN --> <style type="text/css"> input[type="file"] { width:400px; } pre { border:1px #AAA solid; border-radius:10px; padding:10px; } li.list-group-item { padding:5px; } .checkAlert { font-size:120%; } .checkAlert .badge { font-family:system-ui,-apple-system,"Segoe UI",Roboto; } .checkAlert .badge.text-bg-secondary{ letter-spacing:1px; } pre { background-color:#fff9; } </style> <script> const _online_ = {}; function highlightAddr(addr){ return '<b>'+addr.substr(0,4)+'</b><span style="color:#bbb;">'+addr.substr(4,addr.length-8)+'</span><b>'+addr.substr(addr.length - 4)+'</b>'; } function stringify(jsObj){ return JSON.stringify(jsObj, null, 3).replace(/\n/g, "<br/>").replace(/ /g, "&nbsp;&nbsp;&nbsp;"); } $(function(){ $('input[type="radio"][name="ServerRadio"]').change(function() { NETWORK.switchServer(this.value); if (this.value === "main"){ $('#bg-cur').removeClass('bg-danger').addClass('bg-success'); } else { $('#bg-cur').removeClass('bg-success').addClass('bg-danger'); } }); $('input[type="radio"][name="ServerRadio"]:checked').trigger("change"); $('#trxSignedInputFile').on('change', (event) => { var file = event.target.files[0]; if (file) { if (file.type === 'application/json') { var reader = new FileReader(); reader.onload = (e) => { $('#trxSigned').val(e.target.result); $('#sendTrxBtn').removeAttr('disabled'); event.target.value = null;//chrome does not call onChange when filename is same, so reset it //change network if needed var matches = file.name.match(/\-[^\.\-]+/g); if (matches.length >= 1){ $('#network'+matches[0].toLowerCase()).trigger('click', true); } //change server (main/test) if (matches.length >= 2){ $('#server'+matches[1].toLowerCase()).trigger("click"); } _online_.check(e.target.result); }; reader.readAsText(file); } else { alert('Veuillez sélectionner un fichier texte json (.signed.json)'); } } }); $('#trxSigned').on('input', (event) => { $('#sendTrxBtn').removeAttr('disabled'); _online_.check($('#trxSigned').val()); }); if (window.navigator && !window.navigator.onLine){ $('#nav-state-offline').show(); } }); _online_.getTrx = function(signedTrx){ var textArea = $('#trxSigned'); textArea.removeClass("is-invalid"); var tx = textArea.val().trim(); $('.invalid-feedback',textArea.parent()).hide(); if (tx.length == 0){ textArea.addClass("is-invalid"); $('.invalid-feedback',textArea.parent()).text("Veuillez charger un fichier ou copier une transaction signée d'abord !").show(); return; } try { tx = JSON.parse(tx); } catch (error){ console.error(error); textArea.addClass("is-invalid"); $('.invalid-feedback',textArea.parent()).html("Ce contenu n'est pas un objet JSON valid : <b>"+error.toString()+"</b>").show(); return; } return tx; } _online_.check = function(signedTrx){ var tx = _online_.getTrx(); var decoded = NETWORK.decode(tx); var htmlCheck; switch(decoded.type){ case "Payment" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>Transfert:</u></b><br>'+ 'De : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ 'Vers : <span class="badge text-bg-primary">'+highlightAddr(decoded.trx.to)+'</span><br>'+ 'Tag : <span class="badge text-bg-success">'+(decoded.trx.tag?decoded.trx.tag:'Aucun tag')+'</span><br>'+ 'Montant : <span class="badge text-bg-info">'+decoded.trx.amount+' '+decoded.trx.currency+'</span><br>'+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; case "TrustSet" : case "TrustRev" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>'+(decoded.type=="TrustSet"?"Définition":"Révocation")+' d\'une trustline:</u></b><br>'+ 'Compte : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ 'Issuer : <span class="badge text-bg-primary">'+highlightAddr(decoded.trx.issuer)+'</span><br>'+ 'Currency : <span class="badge text-bg-success">'+decoded.trx.currency+'</span><br>'+ (decoded.trx.max==0?'':'Max : <span class="badge text-bg-info">'+decoded.trx.max+'</span><br>')+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; case "AccountDelete" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>Suppression de compte:</u></b><br>'+ 'Compte : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ 'Reliquat vers : <span class="badge text-bg-primary">'+highlightAddr(decoded.trx.to)+'</span><br>'+ 'Tag : <span class="badge text-bg-success">'+(decoded.trx.tag?decoded.trx.tag:'Aucun tag')+'</span><br>'+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; case "SignerListSet" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>Définir signataire:</u></b><br>'+ 'Compte : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ 'Signataire : <span class="badge text-bg-primary">'+highlightAddr(decoded.trx.signer)+'</span><br>'+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; case "SignerListDel" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>Supprimer signataire:</u></b><br>'+ 'Compte : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ (decoded.trx.signer?'Signataire : <span class="badge text-bg-primary">'+highlightAddr(decoded.trx.signer)+'</span><br>':'')+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; case "MasterOff" : case "MasterOn" : htmlCheck = '<div class="checkAlert alert alert-primary" role="alert">'+ '<b><u>'+(decoded.type=="MasterOff"?"Désactiver":"(Ré)activer")+' clé de compte maître:</u></b><br>'+ 'Compte : <span class="badge text-bg-secondary">'+highlightAddr(decoded.trx.from)+'</span><br>'+ 'Frais : <span class="badge text-bg-warning">'+decoded.trx.fee+' '+NETWORK.symbol+'</span>'+ '</div>'; break; default: htmlCheck = '<div>'+ '<b class="text-danger"> Ce type de transaction ne peut être vérifiée ici, mais voici son code pour validation manuelle :</b><br/>'+ '<pre class="mt-3"><code>'+stringify(decoded.trx)+'</code></pre>'+ '</div>'; } var $htmlCheck = $(htmlCheck); if (decoded.trx.multisign){ $htmlCheck.append($('<div><i style="font-size:80%" class="text-secondary">Note : Cette transaction a été signée par un compte signataire et non par le compte maître.</i></div>')); } $('#d_checkTrx').html($htmlCheck.get(0).outerHTML); $('#r_checkTrx').show(); $('#r_send').show(); }; _online_.send = async function(){ $('#d_sending').hide(); $('#trx_OOT').hide(); var tx = _online_.getTrx(); var trx_OOT,trx_OOT_OUT; try { $('#d_sending').show(); trx_OOT = setTimeout(function(){$('#trx_OOT').show();}, 8000); trx_OOT_OUT = setTimeout(function(){ $('#trxFail').html("<b>Erreur:</b> <i>La clé privée qui signe la transaction n'est probablement pas la bonne.</i>"); $('#d_trxKO').show(); $('#sendTrxBtn').removeAttr('disabled'); $('#d_sending').hide(); }, 30000); var dh = document.body.scrollHeight; window.scrollTo(0, dh); $('#d_trxOK').hide(); $('#d_trxKO').hide(); $('#sendTrxBtn').attr('disabled','true'); await NETWORK.connect(); var resp = await NETWORK.send(tx); $('#trxResult').html(stringify(resp)); $('#trxDetailsLink').attr('href',NETWORK.buildTrxLink()); $('#d_trxOK').show(); if (NETWORK.getLastSendSuccess() === true) { $('#okbut').hide(); $('#trxResult').removeClass("text-warning").addClass("text-success"); } else { $('#okbut b').text(NETWORK.getLastSendSuccess()); $('#okbut').show(); $('#trxResult').removeClass("text-success").addClass("text-warning"); } } catch (error){ error = NETWORK.tryReadError(error); console.error(error); $('#trxFail').html(error+"<br/><i>(Voir les logs pour plus de détails)</i>"); $('#d_trxKO').show(); $('#sendTrxBtn').removeAttr('disabled'); } finally { $('#d_sending').hide(); clearTimeout(trx_OOT); clearTimeout(trx_OOT_OUT); window.scrollTo(0, dh+50); } }; </script> </head> <body class="pb-5"> <div class="text-danger fw-bold text-center mt-3" style="display:none;" id="libCompromised"> ATTENTION DANGER !<br/> La librarie du <span>...</span> n'a pas pu être chargée <b>ou est compromise et a été altérée sur le CDN</b>, veuillez ne pas utiliser cette librarie.<br/> Le hash d'integrité peut ne pas correspondre. (Voir les logs pour plus de détails) </div> <div class="container"> <div class="px-4 py-3 my-3 text-center"> <h2 class="display-6 fw-bold">Envoi d'une transaction 3/3<br/><b class="text-success">(Envoi sur la blockchain - ONLINE)</b></h2> <div class="col-lg-8 mx-auto"> <div style="display:none;" class="text-danger" id="nav-state-offline"> <span class="badge text-bg-danger">Vous êtes OFFLINE !</span> Vous devez être connecté à internet pour cette étape.<br/> <i>Rétablissez la connexion internet et rechargez la page.</i><br/> <button class="btn btn-danger mt-3" onclick="window.location.reload();">Recharger la page</button> </div> <p class="lead mb-4"> Cette étape vous permet d'envoyer une transaction signée, quelque soit la transaction. </p> </div> </div> <h4>Chargez le fichier de transaction <u>signée</u> ou collez ici la <u>transaction signée</u></h4> <div class="mb-3"> <label>C'est le fichier ou le contenu que vous avez préparé dans l'étape 2/3 (fichier 2.2)</label><br/> <label for="trxSignedInputFile" class="btn btn-secondary mt-1">Charger le fichier .signed.json</label><input style="visibility:hidden;width:1px;" accept=".signed.json" id="trxSignedInputFile" type="file"/> </div> <div class="mb-3"> <textarea class="form-control" id="trxSigned" rows="8" style="opacity:0.7;"></textarea> <div class="invalid-feedback"></div> </div> <div class="row" id="r_checkTrx" style="display:none;"> <div class="col-8"> <h4 class="text-primary">Vérifiez une dernière fois votre transaction signée !</h4> <div id="d_checkTrx" class="mb-4"> ... </div> </div> </div> <div class="row" id="r_send" style="display:none;"> <div class="col-8"> <div class="alert alert-warning" role="alert"> <i><b>Vous êtes maintenant prêt à envoyer la transaction.</b><br/> Celle-ci va être exécutée sur la blockchain. Êtes-vous sûr ?</i> </div> <button type="button" class="btn btn-success" onclick="_online_.send()" id="sendTrxBtn">Oui! Envoyer la transaction</button> </div> </div> <div id="d_sending" class="text-warning mt-3" style="display:none;"> <div class="spinner-border" style="vertical-align: middle;" role="status"></div> <b>Envoi en cours, veuillez patienter et ne rien toucher...</b><br/> <span style="font-size:90%;" class="text-secondary" id="trx_OOT">Note : si la transaction tourne dans le vide plus de 30s, c'est qu'elle va échouer.<br/><b>→ Vérifiez que vous avez signé avec la bonne clé et que vous avez bien coché la case signataire si vous êtes dans ce cas.</b></span> </div> <div class="row mt-3" id="d_trxOK" style="display:none;"> <div class="col-8"> <span class="text-success">OK ! Votre transaction a réussi.</span> <span class="text-warning" id="okbut" style="display:none;">Mais elle n'a pas pu se terminer correctement sur la blockchain (<b></b>).</span><br/> <a id="trxDetailsLink" class="link-info link-offset-2 link-underline-opacity-25 link-underline-opacity-100-hover" target="_blank" href="https://...">Détails de la transaction...</a><br/> <b><u><a href="javascript:;" onclick="$('#success-details').slideToggle()">Détails du résultat :</a></u></b><br/> <pre id="success-details" class="mt-3 text-success" style="display:none;"><code id="trxResult"></code></pre> </div> </div> <div class="row mt-3" id="d_trxKO" style="display:none;"> <div class="col-8"> <span class="text-danger">Votre transaction a échoué.</span><br/> <b><u>Détails du résultat :</u></b><br/> <pre class="mt-3 text-danger"><code id="trxFail"></code></pre> </div> </div> <ul class="list-group position-absolute m-1" style="width:180px;font-size:90%;right:0;top:0;"> <li class="list-group-item list-group-item-success"> <input class="form-check-input me-1" type="radio" name="ServerRadio" value="main" id="server-main" checked /> <label class="form-check-label" for="server-main">MainNet (En vrai)</label> </li> <li class="list-group-item list-group-item-danger"> <input class="form-check-input me-1" type="radio" name="ServerRadio" value="test" id="server-test"/> <label class="form-check-label" for="server-test">TestNet (Pour tester)</label> </li> </ul> </div><!-- container --> <div class="fixed-bottom text-end m-1" style="width:120px;right:0;left:auto;"><span class="badge text-bg-info p-2"><a id="tuto-video" href="..." target="_blank">Tutoriel vidéo</a></span></div> <h4 class="fixed-top m-1" style="width:150px;"><span id="network-selected" class="badge rounded-pill text-bg-dark">...</span> <div id="networks" style="margin-left:5px;display:none;"> <div> <input id="network-xrpl-radio" type="radio" name="blockchain" value="XRPL" style="display:none;" checked /> <label for="network-xrpl-radio"><span id="network-xrpl" class="nw-selector badge rounded-pill text-bg-secondary" tuto="https://youtu.be/djt5ZKKJrWI">XRPL</span></label> </div> <div> <input id="network-xlm-radio" type="radio" name="blockchain" value="XLM" style="display:none;" /> <label for="network-xlm-radio"><span id="network-xlm" class="nw-selector badge rounded-pill text-bg-secondary" tuto="https://youtu.be/CJpHtLly6n4">XLM</span></label> </div> </div> </h4> <div id="bg-cur" class="fixed-top" style="width:100vw;height:100vh;z-index:-1;opacity:0.055;"> <div class="text-center" style="width:100vw;height:100vh;font-size:100vh;line-height:100vh;margin-top:-7vh;letter-spacing:40px;color:#FFF;"> <span class="cur fw-bold">XRP</span> </div> </div> <style> #network-selected:hover { cursor:pointer; opacity:90%; } #network-selected::after { color:#AAA; display: inline-block; margin-left: .255em; vertical-align: .255em; content: ""; border-top: .3em solid; border-right: .3em solid transparent; border-bottom: 0; border-left: .3em solid transparent; } #network-selected:hover::after { color:#fff; } .nw-selector:hover{ cursor:pointer; opacity:90%; } </style> <script> var $selectedNW = $('#network-selected'); var $networks = $('#networks'); var lastUsedNetwork = $('input[name="blockchain"]:checked').val(); $selectedNW.on("click", function(){ $networks.toggle(); }); for (var netw of Object.keys(CLASSES)) { $('#network-'+netw.toLowerCase()).on('click',function(e, force){ var n = $('#'+($(this).attr('id')+'-radio')).val(); $selectedNW.text(n); $networks.hide(); if (force || lastUsedNetwork != n){ $(document).trigger('NETWORK_CHANGED',[n,$('input[name="ServerRadio"]:checked').val()]); lastUsedNetwork = n; } $('#tuto-video').attr("href",$(this).attr("tuto")); }); } $('#network-'+lastUsedNetwork.toLowerCase()).trigger('click', true); </script> </body> </html>