github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/www/templates/send.html (about)

     1  <script type="text/javascript" src="webui/crypto.js"></script>
     2  <script type="text/javascript" src="webui/bech32.js"></script>
     3  <div id="light" class="white_content">
     4  <table width="100%"><tr>
     5  <td><h3><b class="mono" id="disp_txid"></b></h3>
     6  <td align="right"><img title="Close this popup" src="webui/close.png" class="hand" onclick="closepopup_x(false)">
     7  </table>
     8  <pre id="rawdiv" style="background-color:white"></pre>
     9  </div><div id="fade" class="black_overlay"></div>
    10  
    11  <style>
    12  td.inpid {
    13  	text-align: left;
    14  	font-size: 90%;
    15  	font-family: monospace;
    16  }
    17  td.P2SH-P2WPKH {
    18  	color:brown;
    19  	font-style:italic;
    20  }
    21  td.P2WSH {
    22  	color:purple;
    23  	font-style:italic;
    24  }
    25  td.P2WPKH {
    26  	color:darkgreen;
    27  	font-style:italic;
    28  }
    29  td.P2TAP {
    30  	color:blue;
    31  	font-style:italic;
    32  }
    33  td.long_addr {
    34  	font-size:9px;
    35  }
    36  </style>
    37  <script>
    38  const addrbook_lab = "Address Book"
    39  
    40  const AvgOutputSize = 34
    41  
    42  var selected_value = 0
    43  var ets_inputs = 0
    44  var witness_size = 0
    45  var non_segwit_cnt = 0
    46  var first_input_addr = ''
    47  var selected_cnt = 0
    48  var total_to_pay = 0
    49  var cur_but = null
    50  
    51  var wallet = new Array()
    52  var addrbook = new Array()
    53  var basic_addrbook_len = 0 // without the wallets virgin addrs
    54  var auto_transalte_to_segwit = false
    55  var auto_transalte_to_segwit_p2sh = false
    56  var auto_transalte_to_segwit_tap = false
    57  
    58  const ADDR_LIST_SIZE = 15
    59  
    60  function build_change_list() {
    61  	var virgincounter = 0
    62  	while (changeaddrsel.options.length>1) changeaddrsel.remove(1)
    63  	//changeaddrsel.options = [changeaddrsel.options[0]]
    64  	for (var i=0; i<wallet.length; i++) {
    65  		var wpkh = ''
    66  		var op = document.createElement("option")
    67  		if (wallet[i].virgin) {
    68  			op.text = "* "
    69  		}
    70  
    71  		wpkh = translate_address(wallet[i].addr)
    72  
    73  		if (wpkh != '') {
    74  			op.text += 'SW:' + wallet[i].label
    75  			op.text += " - " + wpkh
    76  			op.value = wpkh
    77  		} else {
    78  			op.text += wallet[i].label
    79  			op.text += " - " + wallet[i].addr
    80  			op.value = wallet[i].addr
    81  		}
    82  
    83  		if (wallet[i].virgin) {
    84  			virgincounter++
    85  			changeaddrsel.add(op, changeaddrsel[virgincounter])
    86  		} else {
    87  			changeaddrsel.add(op)
    88  		}
    89  	}
    90  }
    91  
    92  
    93  function show_input_tx() {
    94  	var aj = ajax()
    95  	var vout = parseInt(this["vout"])
    96  	aj.onreadystatechange=function() {
    97  		if(aj.readyState==4) {
    98  			var sta = xval(aj.responseXML,  "status")
    99  			if (sta!="OK") {
   100  				alert(sta)
   101  				return
   102  			}
   103  			disp_txid.innerHTML = xval(aj.responseXML,  "id")
   104  
   105  			var s = ''
   106  			var tout = 0
   107  
   108  			s += 'Len: ' + xval(aj.responseXML,  "size") + '\n'
   109  			s += "Total inputs: " + aj.responseXML.getElementsByTagName('input').length + "\n"
   110  			s += "\n"
   111  
   112  			var is = aj.responseXML.getElementsByTagName('output')
   113  			for (var i=0; i<is.length; i++) {
   114  				s += (i+1) + ") "
   115  				var val = parseInt(xval(is[i], "value"))
   116  				tout += val
   117  				s += (parseFloat(val)/1e8).toFixed(8)
   118  				s += " BTC => " + xval(is[i], "addr")
   119  				if (i==vout)  s += " <-------------------------------"
   120  				s += "\n"
   121  			}
   122  			s += "Total output: " + (parseFloat(tout)/1e8).toFixed(8) + " BTC\n"
   123  
   124  			rawdiv.innerHTML = s
   125  
   126  			if (prvpos==null) {
   127  				fade.addEventListener('click', closepopup)
   128  				fade.style.cursor = 'pointer'
   129  				fade.title = 'Click here to close the popup'
   130  			}
   131  			prvpos = document.body.scrollTop
   132  			window.scrollTo(0,0)
   133  
   134  			light.style.display='block'
   135  			fade.style.display='block'
   136  			document.addEventListener("scroll", noscroll)
   137  		}
   138  	}
   139  	aj.open("GET","txs2s.xml?minedid="+this["txid"]+'&minedat='+this["block"]+'&sid='+sid, true);
   140  	aj.send(null);
   141  }
   142  
   143  function fetch_wallet_balance(name) {
   144  	var curr_wallet_raw = localStorage.getItem("gocoinWal_"+name)
   145  	auto_transalte_to_segwit = curr_wallet_raw.indexOf("SegWit") != -1
   146  	if (auto_transalte_to_segwit) {
   147  		auto_transalte_to_segwit_p2sh = curr_wallet_raw.indexOf("SegWit P2SH") != -1
   148  		if (!auto_transalte_to_segwit_p2sh) {
   149  			auto_transalte_to_segwit_tap = curr_wallet_raw.indexOf("SegWit Tap") != -1
   150  		}
   151  	}
   152  
   153  	wallet = parse_wallet(curr_wallet_raw)
   154  	var wallet_addr = new Array()
   155  
   156  	for (var i=0; i<wallet.length; i++) {
   157  		wallet_addr.push(wallet[i].addr)
   158  	}
   159  
   160  	while (unspent.rows.length>1) {
   161  		unspent.deleteRow(1)
   162  	}
   163  
   164  	var aj = ajax()
   165  	aj.onerror=function() {
   166  		console.log("onerror")
   167  	}
   168  
   169  	aj.onload=function() {
   170  		try {
   171  			var totbtc = 0
   172  			var totouts = 0
   173  			bal = JSON.parse(aj.responseText)
   174  			var outs = new Array()
   175  			var i, ii
   176  			for (i in wallet) {
   177  				var rec = bal[wallet[i].addr]
   178  				if (typeof(rec)=='undefined') continue
   179  				if (rec.Outs!=null) {
   180  					totouts += rec.Outs.length
   181  					for (ii in rec.Outs) {
   182  						var ty = rec.Outs[ii].AddrType
   183  						if (ty=='P2PKH') {
   184  							var ad = rec.Outs[ii].Addr
   185  							if (ad.substr(0,3)=="bc1" || ad.substr(0,3)=="tb1") {
   186  								ty = ad.length > 45 ? "P2WSH" : "P2WPKH"
   187  							}
   188  						}
   189  						outs.push({'block':rec.Outs[ii].Height, 'txid':rec.Outs[ii].TxId,
   190  							'vout':rec.Outs[ii].Vout, 'value':rec.Outs[ii].Value,
   191  							'addr':wallet[i].addr, 'label':wallet[i].label,
   192  							'addr2':rec.Outs[ii].Addr,  'msg':rec.Outs[ii].Message, 'type':ty})
   193  					}
   194  				}
   195  				totbtc += rec.Value
   196  			}
   197  			outs.sort(function(a,b){
   198  				if (a.block == b.block)  return 0
   199  				return (a.block > b.block) ? 1 : -1;
   200  			})
   201  
   202  			outcnt_val.value = outs.length
   203  			for (i in outs) {
   204  				var row_number = parseInt(i)+1
   205  				var inp,  td, row = unspent.insertRow(-1)
   206  				row.className = "how"
   207  				row["addr"] = outs[i].addr2
   208  				row["value"] = outs[i].value
   209  				row["label"] = outs[i].label
   210  
   211  				inp = document.createElement('input')
   212  				inp.type = 'hidden'
   213  				inp.name = "txid"+row_number
   214  				inp.value = outs[i].txid
   215  				row.appendChild(inp)
   216  
   217  				inp = document.createElement('input')
   218  				inp.type = 'hidden'
   219  				inp.name = "txvout"+row_number
   220  				inp.value = outs[i].vout
   221  				row.appendChild(inp)
   222  
   223  				inp = document.createElement('input')
   224  				inp.type = 'hidden'
   225  				inp.id = "txsigsiz"+row_number
   226  				inp["segwit_size"] = 0
   227  				if (outs[i].type=="P2PKH") {
   228  					inp.value = 72 + 34  // pub_key + signature
   229  				} else if (outs[i].type=="P2WPSH") {
   230  					inp.value = 23  // P2SH script
   231  					inp["segwit_size"] = 1 + 72 + 34 // witness_ver + key + signature
   232  				} else if (outs[i].type=="P2WPKH") {
   233  					inp.value = 0
   234  					inp["segwit_size"] = 1 + 72 + 34 // witness_ver + key + signature
   235  				} else if (outs[i].type=="P2TAP") {
   236  					inp.value = 0
   237  					inp["segwit_size"] = 1 + 1 + 64 + 0  // wit_ver + scr_len + schnorr_signature [+ hash_type]
   238  				} else {
   239  					// default for P2SH and P2WSH (3 keys and 2 signatures)
   240  					inp.value = 2 + 3*34 + 2*73
   241  				}
   242  				row.appendChild(inp)
   243  
   244  				td = row.insertCell(-1)
   245  				td.align = 'right'
   246  				td.innerText = row_number
   247  
   248  				td = row.insertCell(-1)
   249  				td.align = 'right'
   250  				td.innerText = outs[i].block
   251  
   252  				td = row.insertCell(-1)
   253  				td.className = 'inpid hand'
   254  				td.innerText = outs[i].txid + ' - ' + outs[i].vout
   255  				td["txid"] = outs[i].txid
   256  				td["vout"] = outs[i].vout
   257  				td["block"] = outs[i].block
   258  				td.addEventListener('click', show_input_tx)
   259  
   260  				td = row.insertCell(-1)
   261  				td.align = 'right'
   262  				td.className = 'mono bold'
   263  				td.innerText = val2str_pad(outs[i].value,true)
   264  
   265  				td = row.insertCell(-1)
   266  				td.className = 'bold hand'
   267  				td["row_number"] = row_number
   268  				td.addEventListener('click', selectadr)
   269  				td.classList.add(outs[i].type)
   270  				td.innerText = outs[i].addr2
   271  				if (outs[i].addr2.length>45) {
   272  					td.classList.add("long_addr")
   273  				}
   274  				td.title = outs[i].addr
   275  
   276  				td = row.insertCell(-1)
   277  				td.className = 'hand'
   278  				td["row_number"] = row_number
   279  				td.addEventListener('click', selectlabel)
   280  				td.innerText = outs[i].label
   281  
   282  				td = row.insertCell(-1)
   283  				td.className = 'hand'
   284  				if (typeof(outs[i].msg)=='string' && outs[i].msg != '') {
   285  					var img = document.createElement("img")
   286  					img.src = 'webui/message.png'
   287  					img.title = outs[i].msg.substr(0,2)=='j0' ? ('*'+outs[i].msg.substr(2)) : outs[i].msg
   288  					td.appendChild(img)
   289  				}
   290  
   291  				td = row.insertCell(-1)
   292  				td.align = 'center'
   293  				var inp = document.createElement("input")
   294  				inp.id = "txout"+row_number
   295  				inp.type = "checkbox"
   296  				inp.name = "txout"+row_number
   297  				inp.onchange = recalc_inputs
   298  				td.appendChild(inp)
   299  			}
   300  
   301  			total_btc.innerText = val2str(totbtc)
   302  			outs_cnt.innerText = totouts
   303  		} catch (e) {
   304  			console.log(e)
   305  		}
   306  	}
   307  
   308  	aj.open("POST", "balance.json", true)
   309  	aj.send(JSON.stringify(wallet_addr))
   310  }
   311  
   312  function recalc_inputs() {
   313  	selected_value = 0
   314  	selected_cnt = 0
   315  	ets_inputs = 0
   316  	witness_size = 0
   317  	non_segwit_cnt = 0
   318  	first_input_addr = ''
   319  	for (var i=1; i<unspent.rows.length; i++) {
   320  		if (document.getElementById('txout'+i).checked) {
   321  			if (first_input_addr=='')  first_input_addr=unspent.rows[i].addr
   322  			selected_value += unspent.rows[i].value
   323  			selected_cnt++
   324  			var el = document.getElementById('txsigsiz'+i)
   325  			ets_inputs += 36 + 1 + parseFloat(el.value) + 4
   326  			var sws = el["segwit_size"]
   327  			if (sws==0)  non_segwit_cnt++
   328  			else  witness_size += sws
   329  		}
   330  	}
   331  
   332  	selval.innerText = (selected_value/1e8).toFixed(8)
   333  	selcnt.innerText = selected_cnt.toString()
   334  
   335  	paybut.disabled = (selected_cnt==0)
   336  
   337  	recalc_to_pay()
   338  }
   339  
   340  function addr2outsize(ad) {
   341  	if (ad.substr(0,3)=="bc1" || ad.substr(0,3)=="tb1") {
   342  		return 8 + ((ad.length>45) ? 35 : 23)
   343  	}
   344  	switch (ad[0]) {
   345  		case '1':
   346  		case 'm':
   347  		case 'n':
   348  			return 8+1+25
   349  		case '2':
   350  		case '3':
   351  			return 8+1+23
   352  	}
   353  	return AvgOutputSize
   354  }
   355  
   356  
   357  function recalc_to_pay() {
   358  	var el, v, fee, totsend=0
   359  	var butdisabled = false
   360  	var non_segwit_size = 10+ets_inputs // version + v_in + n_out + lock_time
   361  
   362  	fee = val2int(txfee.value)
   363  
   364  	for (var idx=1; idx<outtab.rows.length-3; idx++) {
   365  		el = document.getElementById('out'+idx)
   366  		v = val2int(el.value)
   367  		if (isNaN(v)) {
   368  			el.classList.add('err')
   369  			butdisabled = true
   370  			document.getElementById('mbtc_out'+idx).value = ''
   371  		} else {
   372  			el.classList.remove('err')
   373  			totsend += v
   374  			document.getElementById('mbtc_out'+idx).value = val2str(1000*v)
   375  		}
   376  		var ad = document.getElementById('inadr'+idx).value
   377  		non_segwit_size += addr2outsize(ad)
   378  	}
   379  
   380  	if (include_fee_output_in_size_calculation.checked) {
   381  		var ad = changeaddrsel.selectedIndex
   382  		if (ad!=0) {
   383  			ad = changeaddrsel.options[ad].value
   384  			non_segwit_size += addr2outsize(ad)
   385  		} else {
   386  			non_segwit_size += addr2outsize(first_input_addr)
   387  		}
   388  	}
   389  
   390  	var tx_size = non_segwit_size
   391  	if (witness_size>0) {
   392  		// segwit tx is 2 bytes longer, plus each non-segwit input uses one extra byte (0) in its segwit section
   393  		tx_size += 2+witness_size+non_segwit_cnt
   394  	}
   395  	var vsize = Math.floor((3*(non_segwit_size+1) + tx_size)/4)
   396  	
   397  	ets.innerText = vsize
   398  	
   399  	if (auto_adjust_fee.checked) {
   400  		var spb = parseFloat(spb_to_use.value)
   401  		if (isNaN(spb)) spb = 0
   402  		fee = Math.round(vsize*spb)
   403  		txfee.value = val2str(fee)
   404  	}
   405  
   406  	if (isNaN(fee)) {
   407  		txfee.classList.add('err')
   408  		butdisabled = true
   409  		txfee_mbtc.value = ''
   410  	} else {
   411  		txfee.classList.remove('err')
   412  		txfee_mbtc.value = val2str(1000*fee)
   413  	}
   414  
   415  	var chval = selected_value-totsend-fee
   416  	changeval.value = val2str(chval)
   417  	changeval_mbtc.value = val2str(1000*chval)
   418  	if (chval<0) {
   419  		changeval.style.color = 'red'
   420  		butdisabled = true
   421  		include_fee_out_checkbox.classList.remove('err')
   422  	} else if (chval>0) {
   423  		changeval.style.color = 'green'
   424  		if (!include_fee_output_in_size_calculation.checked) {
   425  			include_fee_out_checkbox.classList.add('err')
   426  		} else {
   427  			include_fee_out_checkbox.classList.remove('err')
   428  		}
   429  	} else {
   430  		changeval.style.color = ''
   431  		if (include_fee_output_in_size_calculation.checked) {
   432  			include_fee_out_checkbox.classList.add('err')
   433  		} else {
   434  			include_fee_out_checkbox.classList.remove('err')
   435  		}
   436  	}
   437  
   438  	paybut.disabled = butdisabled
   439  
   440  	if (vsize<1 || isNaN(fee)) {
   441  		feeperbyte.innerText = '???'
   442  	} else {
   443  		feeperbyte.innerText = (fee/vsize).toFixed(1)
   444  	}
   445  }
   446  
   447  
   448  function auto_adjust_fee_clicked() {
   449  	if (auto_adjust_fee.checked) {
   450  		recalc_to_pay()
   451  	}
   452  }
   453  
   454  function translate_address(t) {
   455  	var pkb = hex2array(t)
   456  	if (pkb!=null && typeof(pkb)=="object" && pkb.length==33 && (pkb[0]==2 || pkb[0]==3)) {
   457  		// public key
   458  		if (auto_transalte_to_segwit_tap) {
   459  			return sw_encode(testnet?"tb":"bc", 1, pkb.slice(1, 33))
   460  		}
   461  		t = pubkey_to_p2kh(pkb)
   462  		// fallback to regulat addr transation
   463  	}
   464  	if (!auto_transalte_to_segwit)  return t
   465  	if (auto_transalte_to_segwit_p2sh)  return p2kh_to_witness_p2sh(t)
   466  	try {
   467  		var r = Base58.decode(t)
   468  		if (25!=r.length || 0!=r[0] && 111!=r[0]) return ""
   469  		return sw_encode(testnet?"tb":"bc", 0, r.slice(1,21))
   470  	} catch (e) {
   471  		return ""
   472  	}
   473  }
   474  
   475  
   476  function open_address_book(idx) {
   477  	if (cur_but!=null) {
   478  		cur_but.click()
   479  	}
   480  
   481  	addrbook = parse_wallet(localStorage.getItem("gocoinAddressBook"))
   482  	basic_addrbook_len = addrbook.length
   483  
   484  	// append the curtrent wallet's virgin addresses
   485  	try { // in case if we had no wallets in the browser
   486  		var tmp = parse_wallet(localStorage.getItem("gocoinWal_"+qswal.options[qswal.selectedIndex].text))
   487  		for (var i in tmp) {
   488  			if (tmp[i].virgin) addrbook.push(tmp[i])
   489  		}
   490  	} catch (ex) {}
   491  
   492  	var c = outtab.rows[idx].cells[1]
   493  	var inadr = document.getElementById('inadr'+idx)
   494  	var div = document.createElement("div")
   495  	c.style.position="realitive"
   496  	div.style.position="absolute"
   497  
   498  	var sel = document.createElement("select")
   499  	sel.style.width = '100%'
   500  	sel.size = ADDR_LIST_SIZE
   501  
   502  	for (var i=0; i<addrbook.length; i++) {
   503  		var op = document.createElement("option")
   504  		var wpkh = ''
   505  
   506  		if (i >= basic_addrbook_len) {
   507  			wpkh = translate_address(addrbook[i].addr)
   508  		}
   509  
   510  		if (wpkh != '') {
   511  			var label = 'SW:' + addrbook[i].label
   512  			op = document.createElement("option")
   513  			op.text += label
   514  			op.text += " - " + wpkh
   515  			op.value = wpkh
   516  			op.selected = inadr.value==op.value
   517  			op["addr_label"] = label
   518  		} else {
   519  			op.text += addrbook[i].label
   520  			op.text += " - " + addrbook[i].addr
   521  			op.value = addrbook[i].addr
   522  			op.selected = inadr.value==op.value
   523  			op["addr_label"] = addrbook[i].label
   524  		}
   525  		sel.add(op)
   526  	}
   527  
   528  	div.appendChild(sel)
   529  	c.appendChild(div)
   530  
   531  	var but = document.getElementById("addrbook"+idx)
   532  	var prv_valu = but.value
   533  	var prv_oncl = but.onclick
   534  	but.value = "Close the list"
   535  	cur_but = but
   536  	but.onclick = sel.onchange = function() {
   537  		c.removeChild(div)
   538  		but.onclick = prv_oncl
   539  		if (sel.selectedIndex!=-1) {
   540  			inadr.value = sel.options[sel.selectedIndex].value
   541  			but.value = sel.options[sel.selectedIndex].addr_label
   542  		} else {
   543  			but.value = addrbook_lab
   544  		}
   545  		cur_but = null
   546  		recalc_to_pay()
   547  	}
   548  }
   549  
   550  
   551  function reslab(idx) {
   552  	var val = document.getElementById("inadr"+idx).value
   553  	var but = document.getElementById("addrbook"+idx)
   554  	for (var i=0; i<addrbook.length; i++) {
   555  		if (val==addrbook[i].addr) {
   556  			but.value = addrbook[i].label
   557  			return
   558  		}
   559  	}
   560  	for (var i=0; i<wallet.length; i++) {
   561  		if (val==wallet[i].addr) {
   562  			but.value = wallet[i].label
   563  			return
   564  		}
   565  	}
   566  	but.value = addrbook_lab
   567  }
   568  
   569  
   570  function del_last_output() {
   571  	if (outtab.rows.length<=5) {
   572  		alert('You cannot remove the only output')
   573  		return
   574  	}
   575  	if (confirm('Remove the last output?')) {
   576  		outtab.deleteRow(outtab.rows.length-4)
   577  		recalc_to_pay()
   578  	}
   579  }
   580  
   581  function add_new_output() {
   582  	var idx = outtab.rows.length-3
   583  	var val, c, r = outtab.insertRow(idx)
   584  
   585  	c = r.insertCell(-1)
   586  	c.innerHTML = 'Out#'+idx
   587  	if (idx>1) {
   588  		c.style.cursor = 'pointer'
   589  		c.title = 'Click to delete this output'
   590  		c.id = idx
   591  		c.onclick = function() {del_output(idx)}
   592  	}
   593  
   594  	c = r.insertCell(-1)
   595  	c.innerHTML = '<input id="inadr'+idx+'" name="adr'+idx+'" size="68" class="mono" onkeyup="reslab('+idx+')" onchange="reslab('+idx+')">'
   596  	c.innerHTML += ' <input type="button" style="width:100px;font-size:12px;padding:2px" value="'+
   597  		addrbook_lab+'" id="addrbook'+idx+'" onclick="open_address_book('+idx+')" tabindex="-1">'
   598  
   599  	// amount BTC
   600  	c = r.insertCell(-1)
   601  	val = document.createElement('input')
   602  	val.type = 'text'
   603  	val.size = 13
   604  	val.id = 'out'+idx
   605  	val.name = 'btc'+idx
   606  	val.onchange = recalc_to_pay
   607  	val.onkeyup = recalc_to_pay
   608  	val.className = 'mono r'
   609  	val.setAttribute("autocomplete","off")
   610  	val.autoComplete = false
   611  	val.value = ''
   612  	c.appendChild(val)
   613  
   614  	// empty cell between BTC and mBTC
   615  	c = r.insertCell(-1)
   616  
   617  	// amount mBTC
   618  	c = r.insertCell(-1)
   619  	val = document.createElement('input')
   620  	val.type = 'text'
   621  	val.size = 13
   622  	val.id = 'mbtc_out'+idx
   623  	val.className = 'mono r dis'
   624  	val.readOnly = true
   625  	val.tabIndex = -1
   626  	c.appendChild(val)
   627  
   628  	recalc_to_pay()
   629  }
   630  
   631  function allchange(t) {
   632  	for (var i=1; i<unspent.rows.length; i++) {
   633  		document.getElementById('txout'+i).checked = t.checked
   634  	}
   635  	recalc_inputs()
   636  }
   637  
   638  function selectadr() {
   639  	var idx = this.row_number
   640  	var addr = unspent.rows[idx].addr
   641  	var chkd = document.getElementById('txout'+idx).checked
   642  	for (var i=1; i<unspent.rows.length; i++) {
   643  		if (unspent.rows[i].addr==addr) {
   644  			document.getElementById('txout'+i).checked = !chkd
   645  		}
   646  	}
   647  	recalc_inputs()
   648  }
   649  
   650  function selectlabel() {
   651  	var idx = this.row_number
   652  	var label = unspent.rows[idx].label
   653  	var chkd = document.getElementById('txout'+idx).checked
   654  	for (var i=1; i<unspent.rows.length; i++) {
   655  		if (unspent.rows[i].label==label) {
   656  			document.getElementById('txout'+i).checked = !chkd
   657  		}
   658  	}
   659  	recalc_inputs()
   660  }
   661  
   662  function final_clicked() {
   663  	if (final_checkbox.checked) {
   664  		tx_seq.value = -2
   665  		tx_seq.readOnly = true
   666  		tx_seq.style.backgroundColor = '#c0c0c0'
   667  	} else {
   668  		tx_seq.value = 0
   669  		tx_seq.readOnly = false
   670  		tx_seq.style.backgroundColor = '#ffffff'
   671  	}
   672  }
   673  
   674  document.addEventListener('DOMContentLoaded', function() {
   675  	add_new_output()
   676  	txfee.onchange = recalc_to_pay
   677  	txfee.onkeyup = recalc_to_pay
   678  	// use avg_fee_spb value, but randomly modyfied by up to +/- 10%, for user's privacy
   679  	spb_to_use.value = (Math.random()/5+0.9)*avg_fee_spb.toFixed(10).substr(0,7)
   680  	recalc_inputs()
   681  	var abc = localStorage.getItem("gocoinAddressBook")
   682  	if (typeof(abc)!="string") {
   683  		abc = "# Empty Address Book"
   684  		localStorage.setItem("gocoinAddressBook", abc)
   685  	}
   686  	address_book.value = abc
   687  })
   688  
   689  function edit_address_book() {
   690  	addrr_book_div.style.display='block'
   691  	addrbook_button.value = "Save Address Book"
   692  	addrbook_button.onclick = function() {save_address_book()}
   693  }
   694  
   695  function save_address_book() {
   696  	localStorage.setItem("gocoinAddressBook", address_book.value)
   697  	addrr_book_div.style.display='none'
   698  	addrbook_button.value = "Edit Address Book"
   699  	addrbook_button.onclick = function() {edit_address_book()}
   700  }
   701  
   702  </script>
   703  
   704  <form method="post" action="payment.zip">
   705  <input type="hidden" id="outcnt_val" name="outcnt" value="">
   706  <h2>Payment details
   707  <input style="float:right;" id="addrbook_button" type="button" value="Edit Address Book" onclick="edit_address_book()">
   708  </h2>
   709  
   710  <div id="addrr_book_div" style="display:none">
   711  <textarea id="address_book" style="width:100%" rows="15"></textarea>
   712  <br><br>
   713  </div>
   714  
   715  Selected amount: <b id="selval">0.00000000</b> BTC in <b id="selcnt">0</b> outputs.
   716  <table class="bord" vspace="10" id="outtab">
   717  <tr>
   718  	<th>&nbsp;
   719  	<th>Pay to address
   720  	<th>Amount BTC
   721  	<th>
   722  	<th><i>... mBTC</i>
   723  </tr>
   724  
   725  <tr>
   726  	<td colspan="2">
   727  	<table width="100%"><tr>
   728  	<td><a href="javascript:add_new_output()" title="Add another output">+add output</a>
   729  	|
   730  	<a href="javascript:del_last_output()" title="Remove last output">-remove last</a>
   731  	<td align="center" title="Transaction sequence for RBF purpose">Sequence : <input name="tx_seq" id="tx_seq" class="mono r" size="2" value="0">
   732  	&nbsp;&bull;&nbsp;
   733  	<span onclick="final_checkbox.click()" class="hand">
   734  		<input onchange="final_clicked()" type="checkbox" id="final_checkbox" onclick="event.stopPropagation()">Final
   735  	</span>
   736  	<td align="right">Transaction fee:
   737  	</table>
   738  	<td><input type="text" id="txfee" name="txfee" size="13" class="mono r" value="0.0001" onchange="recalc_to_pay" onkeyup="recalc_to_pay">
   739  	<td style="font-size:10px" title="Fee per byte"><b id="feeperbyte"></b><br>SPB
   740  	<td><input type="text" id="txfee_mbtc" size="13" class="mono r dis" readonly="readonly" tabindex="-1">
   741  </tr>
   742  
   743  <tr title="Transaction change">
   744  	<td>Change
   745  	<td><select name="change" style="width:100%" id="changeaddrsel" onchange="recalc_to_pay()">
   746  			<option value="">The first input's address</option>
   747  		</select>
   748  	<td class="r">
   749  	<input type="text" class="mono r dis" id="changeval" size="13" readonly="readonly" onclick="this.select()">
   750  	<td id="include_fee_out_checkbox"><input type="checkbox" id="include_fee_output_in_size_calculation" title="Include in size calculation" onchange="recalc_to_pay()">
   751  	<td class="r">
   752  	<input type="text" class="mono r dis" id="changeval_mbtc" size="13" readonly="readonly" tabindex="-1">
   753  </tr>
   754  
   755  <tr>
   756  	<td colspan="5" align="left">
   757  		<input type="checkbox" title="auto adjust the fee" id="auto_adjust_fee" checked="checked" onchange="auto_adjust_fee_clicked()">
   758  		Auto-calc transaction fee using price of&nbsp;
   759  		<input type="text" id="spb_to_use" class="mono r" size="7" onchange="recalc_to_pay()"> Satoshis Per Byte.
   760  		&nbsp;&nbsp;&nbsp;
   761  		Estimated virtual transaction size: <span id="ets" style="font-weight:bold"></span> bytes
   762  	<hr>
   763  		<input type="submit" id="paybut" disabled="disabled" value="Download payment.zip" style="width:100%">
   764  	</td>
   765  </tr>
   766  </table>
   767  <i><b>Note:</b> all the inputs selected below will be combined within one transaction, despite of the amounts entered above.</i>
   768  
   769  <h2>Select Inputs</h2>
   770  Wallet: <select id="qswal" onchange="quick_switch_wallet()"></select>
   771  -
   772  total balance of <b id="total_btc"></b> BTC in <b id="outs_cnt"></b> outputs.
   773  <i id="wallet_min_warning" style="color:purple;float:right;display:none" title="Modify MinValue in gocoin.conf to change it">
   774  <img src="webui/warning.png" style="vertical-align:middle">
   775  Only accounting outputs with at least <b id="min_val_btc"></b> BTC</i>
   776  <br><br>
   777  <table id="unspent" width="100%">
   778  <tr>
   779  	<th width="20">#
   780  	<th width="40">Block
   781  	<th>TxID - VOut
   782  	<th width="80">BTC Value
   783  	<th colspan="2">Address
   784  	<th>&nbsp;
   785  	<th><input type="checkbox" onchange="allchange(this)">
   786  </tr>
   787  <!--UTXOROW-->
   788  </table>
   789  
   790  </form>
   791  <script>
   792  
   793  if (typeof(localStorage.gocoinWallets)=="string") {
   794  	build_wallet_list()
   795  } else {
   796  	localStorage.gocoinWallets = ""
   797  }
   798  
   799  
   800  var warning_set = false
   801  blno.addEventListener("lastblock", function(e) {
   802  	if (!e.block.WalletON) {
   803  		location.reload()
   804  		return
   805  	}
   806  	if (!warning_set) {
   807  		warning_set = true
   808  		if (e.block.MinValue>0) {
   809  			min_val_btc.innerText = val2str(e.block.MinValue)
   810  			wallet_min_warning.style.display = 'block'
   811  		}
   812  	}
   813  })
   814  qswal.addEventListener("loadwallet", function(e) {
   815  	fetch_wallet_balance(e.name)
   816  	build_change_list()
   817  })
   818  document.addEventListener('DOMContentLoaded', function() {
   819  	quick_switch_wallet() // this is to force loading wallet after loading
   820  	addrbook = parse_wallet(localStorage.getItem("gocoinAddressBook"))
   821  	window.onkeyup = function (event) {
   822  		if(event.keyCode == 27)  closepopup_x(false)
   823  	}
   824  })
   825  </script>