/* @licstart The following is the entire license notice for the JavaScript code in this page. Copyright (C) 2015, 2016 INRIA The JavaScript code in this page is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (GNU LGPL) as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details. As additional permission under GNU LGPL version 2.1 section 7, you may distribute non-source (e.g., minimized or compacted) forms of that code without the copy of the GNU LGPL normally required by section 4, provided you include this license notice and a URL through which recipients can access the Corresponding Source. @licend The above is the entire license notice for the JavaScript code in this page. @author Marcello Stanisci */ "use strict"; var FRACTION = 100000000; var START = 0; var DELTA = 5 var LAST = 0; var SCROLL = true; /** * This function *could* "type check" 'amount', * but once 'amount' got here it is already "too late", * because this means that the merchant *backend* gave * it wrong. */ function amount_to_string(amount){ if (typeof amount === 'string' || amount instanceof String) { var split = amount.match(/([A-Z]+):([0-9]+)\.?([0-9]+)?/); if (!split) return "Bad amount given: " + amount; if (split[3]) return `${split[2]}.${split[3]} ${split[1]}`; return `${split[2]}.00 ${split[1]}`; } var number = Number(amount.value) + (Number(amount.fraction)/FRACTION); return `${number.toFixed(2)} ${amount.currency}`; } function close_popup(){ var ctx = document.getElementsByClassName("track-content")[0]; var tbody = xpath_get("table/tbody", ctx).snapshotItem(0); var tbody_children = xpath_get("table/tbody/tr", ctx); for(var i=1; i${msg}

`; info_bar.style.visibility = "visible"; /* Info bar will disappear at next page load/reload. */ } /** * Call /track/order API offered by the frontend. Once data * arrives it calls a UI routine which fills the "entries table" * in the page. */ var track_order = function(order_id, cb){ /* Remove any previous errors, if there are any. */ var info_bar = document.getElementById("information-bar"); info_bar.style.visibility = "hidden"; /* Will be untoggled by the 'cb' */ toggle_loader(); var req = new XMLHttpRequest(); var url = `/track/order?` + `order_id=${order_id}&` + `instance=${get_instance()}`; req.open("GET", url, true); req.onload = function(){ if (4 == req.readyState){ if ((200 == req.status) || (202 == req.status)) cb(JSON.parse(req.responseText), req.status); else show_error(req.responseText, true); return; } } req.send(); } /** * Fill the screen-centered box with data from /track/order. */ function fill_box(tracks, http_code) { toggle_loader(); if (http_code == 404){ alert("No tracks for that order."); return; } if (http_code == 202){ console.log("Pending order."); toggle_loader(); show_warning("This order is still waiting to be paid back"); return; } if(!tracks) console.log("Got invalid JSON"); if(0 == tracks.length || !tracks.length){ console.log(`Got no tracks and status == ${http_code}. ` + `Should not be here.`); return; } for(var i=0; i` + `${subject.substring(0, 20)}...` + `` + `` + `` + `${amount_to_string(entry.amount)}` + `` + `` + `${parse_date(entry.execution_time)}` + ``; table.appendChild(row); toggle_overlay(); } } /** * Helper function which abstracts the hard-to-remember * API offered by document.evaluate(). */ function xpath_get(xpath, ctx){ var ret = document.evaluate (xpath, ctx, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); return ret; } function fill_table(data, execution_time, wtid_marker){ var table = document.getElementById("history"); var tbody = xpath_get("tbody", table).snapshotItem(0); /* Make table's headers visible */ xpath_get("tr[@class='headers']", tbody) .snapshotItem(0).style.visibility = "visible"; /* Will only be effective on page first load; afterwards * it is just idempotent. */ table.style.visibility = "visible"; for (var i=0; i ${entry.order_id}`; td_summary.className = "summary"; td_summary.innerHTML = entry.summary || "deposited"; td_amount.innerHTML = amount_to_string( entry.amount || entry.deposit_value); td_date.innerHTML = parse_date (entry.timestamp || execution_time); row.appendChild(td_order_id); row.appendChild(td_summary); row.appendChild(td_amount); row.appendChild(td_date); row_summary.appendChild(td_summary); tbody.appendChild(row); tbody.appendChild(row_summary); } if (wtid_marker){ // close popup showing wire transfer information close_popup(); // draw a line at the bottom, mentioning the WTID. var marker = make_marker(wtid_marker); tbody.appendChild(marker); } toggle_loader(); } /** * Make the spinning wheel appear if it is not shown, * and viceversa. */ function toggle_loader(){ var loader = document.getElementsByClassName("loader")[0]; if (loader.style.visibility != "hidden") { loader.style.visibility = "hidden"; console.log("toggle_loader: visible>hidden"); } else { loader.style.visibility = "visible"; console.log("toggle_loader: hidden>visible"); } } /** * Issue a /track/order (/track/transfer) depending on * whether the user selected "order" ("transfer") on the * given form. */ function track_cherry_pick(form){ var types = xpath_get("input[@type='radio']", form); for(var i in [0, 1]){ if (!types.snapshotItem(i).checked) continue; var type = types.snapshotItem(i).value; if ("order" == type){ var order_id = xpath_get("input[@class='order']", form) .snapshotItem(0); track_order(order_id.value, fill_box); } else{ var data = xpath_get("input[@class='transfer']", form); var wtid = data.snapshotItem(0); var exchange = data.snapshotItem(1); track_transfer(exchange.value, wtid.value, fill_table); } } } /** * The "cherry pick" form allows the user to track a particular * order or wire transfer by entering its "id" in the input field. * * The UI is such that a radio button can switch the form "type" * to query for a order or for a wire transfer; the following two * functions help to switch the form appearance according to this * order/wire-transfer switch. */ /** * Make the "cherry pick" form be suitable to query /track/order. */ function cherry_pick_form_order(form){ var input_order = xpath_get("input[@class='order']", form) .snapshotItem(0); input_order.style.visibility = ""; var input_transfer = xpath_get("input[@class='transfer']", form); for(var i in [0, 1]) input_transfer.snapshotItem(i).style.visibility = "hidden"; } /** * Make the "cherry pick" form be suitable to query /track/transfer. */ function cherry_pick_form_transfer(form){ var input_order = xpath_get("input[@class='order']", form) .snapshotItem(0); input_order.style.visibility = "hidden"; var input_transfer = xpath_get("input[@class='transfer']", form); for(var i in [0, 1]) input_transfer.snapshotItem(i).style.visibility = ""; } /** * Retrieve current istance being tracked. */ function get_instance(){ var select = document.getElementById("instance"); return select.value; } /** * Remove tracks from the main page table, but do NOT remove * the table headers */ function clear_results(){ var table = document.getElementById("history"); var tbody = xpath_get("tbody", table).snapshotItem(0); var tbody_children = xpath_get("tbody/*[position() > 1]", table); for(var i=0; i= document.body.offsetHeight) && SCROLL) get_history(true, fill_table); }); /* Close dialog on ESC press */ document.onkeydown = function(e) { if(!e) e = event; if(e.keyCode == 27){ close_popup(); } } if (typeof(module) != "undefined"){ module.exports.track_transfer = track_transfer; module.exports.track_order = track_order; }