Merge branch 'master' of git+ssh://taler.net/var/git/wallet-webex
This commit is contained in:
commit
4974dd19c0
BIN
articles/ui/ui.pdf
Normal file
BIN
articles/ui/ui.pdf
Normal file
Binary file not shown.
@ -31,24 +31,149 @@
|
|||||||
namespace TalerNotify {
|
namespace TalerNotify {
|
||||||
const PROTOCOL_VERSION = 1;
|
const PROTOCOL_VERSION = 1;
|
||||||
|
|
||||||
console.log("Taler injected", chrome.runtime.id);
|
/**
|
||||||
|
* Wallet-internal version of offerContractFrom, used for 402 payments.
|
||||||
|
*/
|
||||||
|
function internalOfferContractFrom(url: string) {
|
||||||
|
function handle_contract(contract_wrapper: any) {
|
||||||
|
var cEvent = new CustomEvent("taler-confirm-contract", {
|
||||||
|
detail: {
|
||||||
|
contract_wrapper: contract_wrapper,
|
||||||
|
replace_navigation: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.dispatchEvent(cEvent);
|
||||||
|
}
|
||||||
|
|
||||||
function subst(url: string, H_contract) {
|
var contract_request = new XMLHttpRequest();
|
||||||
|
console.log("downloading contract from '" + url + "'");
|
||||||
|
contract_request.open("GET", url, true);
|
||||||
|
contract_request.onload = function (e) {
|
||||||
|
if (contract_request.readyState == 4) {
|
||||||
|
if (contract_request.status == 200) {
|
||||||
|
console.log("response text:",
|
||||||
|
contract_request.responseText);
|
||||||
|
var contract_wrapper = JSON.parse(contract_request.responseText);
|
||||||
|
if (!contract_wrapper) {
|
||||||
|
console.error("response text was invalid json");
|
||||||
|
alert("Failure to download contract (invalid json)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handle_contract(contract_wrapper);
|
||||||
|
} else {
|
||||||
|
alert("Failure to download contract from merchant " +
|
||||||
|
"(" + contract_request.status + "):\n" +
|
||||||
|
contract_request.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
contract_request.onerror = function (e) {
|
||||||
|
alert("Failure requesting the contract:\n"
|
||||||
|
+ contract_request.statusText);
|
||||||
|
};
|
||||||
|
contract_request.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wallet-internal version of executeContract, used for 402 payments.
|
||||||
|
*
|
||||||
|
* Even though we're inside a content script, we send events to the dom
|
||||||
|
* to avoid code duplication.
|
||||||
|
*/
|
||||||
|
function internalExecuteContract(contractHash: string, payUrl: string,
|
||||||
|
offerUrl: string) {
|
||||||
|
/**
|
||||||
|
* Handle a failed payment.
|
||||||
|
*
|
||||||
|
* Try to notify the wallet first, before we show a potentially
|
||||||
|
* synchronous error message (such as an alert) or leave the page.
|
||||||
|
*/
|
||||||
|
function handleFailedPayment(status: any) {
|
||||||
|
const msg = {
|
||||||
|
type: "payment-failed",
|
||||||
|
detail: {},
|
||||||
|
};
|
||||||
|
chrome.runtime.sendMessage(msg, (resp) => {
|
||||||
|
alert("payment failed");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleResponse(evt: CustomEvent) {
|
||||||
|
console.log("handling taler-notify-payment");
|
||||||
|
// Payment timeout in ms.
|
||||||
|
let timeout_ms = 1000;
|
||||||
|
// Current request.
|
||||||
|
let r: XMLHttpRequest | null = null;
|
||||||
|
let timeoutHandle: number|null = null;
|
||||||
|
function sendPay() {
|
||||||
|
r = new XMLHttpRequest();
|
||||||
|
r.open("post", payUrl);
|
||||||
|
r.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
r.send(JSON.stringify(evt.detail.payment));
|
||||||
|
r.onload = function() {
|
||||||
|
if (!r) {
|
||||||
|
throw Error("invariant");
|
||||||
|
}
|
||||||
|
switch (r.status) {
|
||||||
|
case 200:
|
||||||
|
window.location.href = subst(evt.detail.contract.fulfillment_url,
|
||||||
|
evt.detail.H_contract);
|
||||||
|
window.location.reload(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
handleFailedPayment(r.status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
r = null;
|
||||||
|
if (timeoutHandle != null) {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
timeoutHandle = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function retry() {
|
||||||
|
if (r) {
|
||||||
|
r.abort();
|
||||||
|
r = null;
|
||||||
|
}
|
||||||
|
timeout_ms = Math.min(timeout_ms * 2, 10 * 1000);
|
||||||
|
console.log("sendPay timed out, retrying in ", timeout_ms, "ms");
|
||||||
|
sendPay();
|
||||||
|
}
|
||||||
|
timeoutHandle = setTimeout(retry, timeout_ms);
|
||||||
|
}
|
||||||
|
sendPay();
|
||||||
|
}
|
||||||
|
|
||||||
|
let detail = {
|
||||||
|
H_contract: contractHash,
|
||||||
|
offering_url: offerUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("taler-notify-payment", handleResponse, false);
|
||||||
|
let eve = new CustomEvent('taler-execute-contract', {detail: detail});
|
||||||
|
document.dispatchEvent(eve);
|
||||||
|
}
|
||||||
|
|
||||||
|
function subst(url: string, H_contract: string) {
|
||||||
url = url.replace("${H_contract}", H_contract);
|
url = url.replace("${H_contract}", H_contract);
|
||||||
url = url.replace("${$}", "$");
|
url = url.replace("${$}", "$");
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlers = [];
|
interface Handler {
|
||||||
|
type: string;
|
||||||
|
listener: (e: CustomEvent) => void;
|
||||||
|
}
|
||||||
|
const handlers: Handler[] = [];
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
chrome.runtime.sendMessage({type: "ping"}, () => {
|
chrome.runtime.sendMessage({type: "ping"}, (resp) => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
console.log("extension not yet ready");
|
console.log("extension not yet ready");
|
||||||
window.setTimeout(init, 200);
|
window.setTimeout(init, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("got pong");
|
|
||||||
registerHandlers();
|
registerHandlers();
|
||||||
// Hack to know when the extension is unloaded
|
// Hack to know when the extension is unloaded
|
||||||
let port = chrome.runtime.connect();
|
let port = chrome.runtime.connect();
|
||||||
@ -59,15 +184,27 @@ namespace TalerNotify {
|
|||||||
document.removeEventListener(handler.type, handler.listener);
|
document.removeEventListener(handler.type, handler.listener);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (resp && resp.type === "fetch") {
|
||||||
|
console.log("it's fetch");
|
||||||
|
internalOfferContractFrom(resp.contractUrl);
|
||||||
|
document.documentElement.style.visibility = "hidden";
|
||||||
|
|
||||||
|
} else if (resp && resp.type === "execute") {
|
||||||
|
console.log("it's execute");
|
||||||
|
document.documentElement.style.visibility = "hidden";
|
||||||
|
internalExecuteContract(resp.contractHash, resp.payUrl, resp.offerUrl);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("loading Taler content script");
|
||||||
init();
|
init();
|
||||||
|
|
||||||
function registerHandlers() {
|
function registerHandlers() {
|
||||||
const $ = (x) => document.getElementById(x);
|
function addHandler(type: string, listener: (e: CustomEvent) => void) {
|
||||||
|
|
||||||
function addHandler(type, listener) {
|
|
||||||
document.addEventListener(type, listener);
|
document.addEventListener(type, listener);
|
||||||
handlers.push({type, listener});
|
handlers.push({type, listener});
|
||||||
}
|
}
|
||||||
@ -193,7 +330,7 @@ namespace TalerNotify {
|
|||||||
console.log("got resp");
|
console.log("got resp");
|
||||||
console.dir(resp);
|
console.dir(resp);
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
console.log("got event detial:");
|
console.log("got event detail:");
|
||||||
console.dir(e.detail);
|
console.dir(e.detail);
|
||||||
if (e.detail.offering_url) {
|
if (e.detail.offering_url) {
|
||||||
console.log("offering url", e.detail.offering_url);
|
console.log("offering url", e.detail.offering_url);
|
||||||
|
@ -117,7 +117,7 @@ const paths = {
|
|||||||
|
|
||||||
|
|
||||||
const tsBaseArgs = {
|
const tsBaseArgs = {
|
||||||
target: "es5",
|
target: "es6",
|
||||||
jsx: "react",
|
jsx: "react",
|
||||||
experimentalDecorators: true,
|
experimentalDecorators: true,
|
||||||
module: "system",
|
module: "system",
|
||||||
@ -125,6 +125,8 @@ const tsBaseArgs = {
|
|||||||
noLib: true,
|
noLib: true,
|
||||||
noImplicitReturns: true,
|
noImplicitReturns: true,
|
||||||
noFallthroughCasesInSwitch: true,
|
noFallthroughCasesInSwitch: true,
|
||||||
|
strictNullChecks: true,
|
||||||
|
noImplicitAny: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
4440
lib/decl/chrome/chrome.d.ts
vendored
4440
lib/decl/chrome/chrome.d.ts
vendored
File diff suppressed because it is too large
Load Diff
16944
lib/decl/lib.es6.d.ts
vendored
16944
lib/decl/lib.es6.d.ts
vendored
File diff suppressed because it is too large
Load Diff
2
lib/decl/systemjs/systemjs.d.ts
vendored
2
lib/decl/systemjs/systemjs.d.ts
vendored
@ -9,7 +9,7 @@ interface System {
|
|||||||
config: any;
|
config: any;
|
||||||
newModule(obj: Object): any;
|
newModule(obj: Object): any;
|
||||||
normalizeSync(name: string): string;
|
normalizeSync(name: string): string;
|
||||||
set(moduleName: string, module: any)
|
set(moduleName: string, module: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
199
lib/decl/webrtc/MediaStream.d.ts
vendored
199
lib/decl/webrtc/MediaStream.d.ts
vendored
@ -1,199 +0,0 @@
|
|||||||
// Type definitions for WebRTC
|
|
||||||
// Project: http://dev.w3.org/2011/webrtc/
|
|
||||||
// Definitions by: Ken Smith <https://github.com/smithkl42/>
|
|
||||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
|
||||||
|
|
||||||
// Taken from http://dev.w3.org/2011/webrtc/editor/getusermedia.html
|
|
||||||
// version: W3C Editor's Draft 29 June 2015
|
|
||||||
|
|
||||||
interface ConstrainBooleanParameters {
|
|
||||||
exact?: boolean;
|
|
||||||
ideal?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NumberRange {
|
|
||||||
max?: number;
|
|
||||||
min?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConstrainNumberRange extends NumberRange {
|
|
||||||
exact?: number;
|
|
||||||
ideal?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConstrainStringParameters {
|
|
||||||
exact?: string | string[];
|
|
||||||
ideal?: string | string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaStreamConstraints {
|
|
||||||
video?: boolean | MediaTrackConstraints;
|
|
||||||
audio?: boolean | MediaTrackConstraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module W3C {
|
|
||||||
type LongRange = NumberRange;
|
|
||||||
type DoubleRange = NumberRange;
|
|
||||||
type ConstrainBoolean = boolean | ConstrainBooleanParameters;
|
|
||||||
type ConstrainNumber = number | ConstrainNumberRange;
|
|
||||||
type ConstrainLong = ConstrainNumber;
|
|
||||||
type ConstrainDouble = ConstrainNumber;
|
|
||||||
type ConstrainString = string | string[] | ConstrainStringParameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaTrackConstraints extends MediaTrackConstraintSet {
|
|
||||||
advanced?: MediaTrackConstraintSet[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaTrackConstraintSet {
|
|
||||||
width?: W3C.ConstrainLong;
|
|
||||||
height?: W3C.ConstrainLong;
|
|
||||||
aspectRatio?: W3C.ConstrainDouble;
|
|
||||||
frameRate?: W3C.ConstrainDouble;
|
|
||||||
facingMode?: W3C.ConstrainString;
|
|
||||||
volume?: W3C.ConstrainDouble;
|
|
||||||
sampleRate?: W3C.ConstrainLong;
|
|
||||||
sampleSize?: W3C.ConstrainLong;
|
|
||||||
echoCancellation?: W3C.ConstrainBoolean;
|
|
||||||
latency?: W3C.ConstrainDouble;
|
|
||||||
deviceId?: W3C.ConstrainString;
|
|
||||||
groupId?: W3C.ConstrainString;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaTrackSupportedConstraints {
|
|
||||||
width?: boolean;
|
|
||||||
height?: boolean;
|
|
||||||
aspectRatio?: boolean;
|
|
||||||
frameRate?: boolean;
|
|
||||||
facingMode?: boolean;
|
|
||||||
volume?: boolean;
|
|
||||||
sampleRate?: boolean;
|
|
||||||
sampleSize?: boolean;
|
|
||||||
echoCancellation?: boolean;
|
|
||||||
latency?: boolean;
|
|
||||||
deviceId?: boolean;
|
|
||||||
groupId?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaStream extends EventTarget {
|
|
||||||
id: string;
|
|
||||||
active: boolean;
|
|
||||||
|
|
||||||
onactive: EventListener;
|
|
||||||
oninactive: EventListener;
|
|
||||||
onaddtrack: (event: MediaStreamTrackEvent) => any;
|
|
||||||
onremovetrack: (event: MediaStreamTrackEvent) => any;
|
|
||||||
|
|
||||||
clone(): MediaStream;
|
|
||||||
stop(): void;
|
|
||||||
|
|
||||||
getAudioTracks(): MediaStreamTrack[];
|
|
||||||
getVideoTracks(): MediaStreamTrack[];
|
|
||||||
getTracks(): MediaStreamTrack[];
|
|
||||||
|
|
||||||
getTrackById(trackId: string): MediaStreamTrack;
|
|
||||||
|
|
||||||
addTrack(track: MediaStreamTrack): void;
|
|
||||||
removeTrack(track: MediaStreamTrack): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaStreamTrackEvent extends Event {
|
|
||||||
track: MediaStreamTrack;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare enum MediaStreamTrackState {
|
|
||||||
"live",
|
|
||||||
"ended"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaStreamTrack extends EventTarget {
|
|
||||||
id: string;
|
|
||||||
kind: string;
|
|
||||||
label: string;
|
|
||||||
enabled: boolean;
|
|
||||||
muted: boolean;
|
|
||||||
remote: boolean;
|
|
||||||
readyState: MediaStreamTrackState;
|
|
||||||
|
|
||||||
onmute: EventListener;
|
|
||||||
onunmute: EventListener;
|
|
||||||
onended: EventListener;
|
|
||||||
onoverconstrained: EventListener;
|
|
||||||
|
|
||||||
clone(): MediaStreamTrack;
|
|
||||||
|
|
||||||
stop(): void;
|
|
||||||
|
|
||||||
getCapabilities(): MediaTrackCapabilities;
|
|
||||||
getConstraints(): MediaTrackConstraints;
|
|
||||||
getSettings(): MediaTrackSettings;
|
|
||||||
applyConstraints(constraints: MediaTrackConstraints): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaTrackCapabilities {
|
|
||||||
width: number | W3C.LongRange;
|
|
||||||
height: number | W3C.LongRange;
|
|
||||||
aspectRatio: number | W3C.DoubleRange;
|
|
||||||
frameRate: number | W3C.DoubleRange;
|
|
||||||
facingMode: string;
|
|
||||||
volume: number | W3C.DoubleRange;
|
|
||||||
sampleRate: number | W3C.LongRange;
|
|
||||||
sampleSize: number | W3C.LongRange;
|
|
||||||
echoCancellation: boolean[];
|
|
||||||
latency: number | W3C.DoubleRange;
|
|
||||||
deviceId: string;
|
|
||||||
groupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaTrackSettings {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
aspectRatio: number;
|
|
||||||
frameRate: number;
|
|
||||||
facingMode: string;
|
|
||||||
volume: number;
|
|
||||||
sampleRate: number;
|
|
||||||
sampleSize: number;
|
|
||||||
echoCancellation: boolean;
|
|
||||||
latency: number;
|
|
||||||
deviceId: string;
|
|
||||||
groupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaStreamError {
|
|
||||||
name: string;
|
|
||||||
message: string;
|
|
||||||
constraintName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NavigatorGetUserMedia {
|
|
||||||
(constraints: MediaStreamConstraints,
|
|
||||||
successCallback: (stream: MediaStream) => void,
|
|
||||||
errorCallback: (error: MediaStreamError) => void): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Navigator {
|
|
||||||
getUserMedia: NavigatorGetUserMedia;
|
|
||||||
|
|
||||||
webkitGetUserMedia: NavigatorGetUserMedia;
|
|
||||||
|
|
||||||
mozGetUserMedia: NavigatorGetUserMedia;
|
|
||||||
|
|
||||||
msGetUserMedia: NavigatorGetUserMedia;
|
|
||||||
|
|
||||||
mediaDevices: MediaDevices;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaDevices {
|
|
||||||
getSupportedConstraints(): MediaTrackSupportedConstraints;
|
|
||||||
|
|
||||||
getUserMedia(constraints: MediaStreamConstraints): Promise<MediaStream>;
|
|
||||||
enumerateDevices(): Promise<MediaDeviceInfo[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MediaDeviceInfo {
|
|
||||||
label: string;
|
|
||||||
id: string;
|
|
||||||
kind: string;
|
|
||||||
facing: string;
|
|
||||||
}
|
|
7
lib/emscripten/emsc.d.ts
vendored
7
lib/emscripten/emsc.d.ts
vendored
@ -33,7 +33,7 @@ export interface EmscFunGen {
|
|||||||
export declare namespace Module {
|
export declare namespace Module {
|
||||||
var cwrap: EmscFunGen;
|
var cwrap: EmscFunGen;
|
||||||
|
|
||||||
function _free(ptr: number);
|
function _free(ptr: number): void;
|
||||||
|
|
||||||
function _malloc(n: number): number;
|
function _malloc(n: number): number;
|
||||||
|
|
||||||
@ -41,9 +41,10 @@ export declare namespace Module {
|
|||||||
|
|
||||||
function getValue(ptr: number, type: string, noSafe?: boolean): number;
|
function getValue(ptr: number, type: string, noSafe?: boolean): number;
|
||||||
|
|
||||||
function setValue(ptr: number, value: number, type: string, noSafe?: boolean);
|
function setValue(ptr: number, value: number, type: string,
|
||||||
|
noSafe?: boolean): void;
|
||||||
|
|
||||||
function writeStringToMemory(s: string,
|
function writeStringToMemory(s: string,
|
||||||
buffer: number,
|
buffer: number,
|
||||||
dontAddNull?: boolean);
|
dontAddNull?: boolean): void;
|
||||||
}
|
}
|
24
lib/i18n.ts
24
lib/i18n.ts
@ -24,14 +24,14 @@ document.addEventListener(
|
|||||||
|
|
||||||
declare var i18n: any;
|
declare var i18n: any;
|
||||||
|
|
||||||
const JedModule = window["Jed"];
|
const JedModule: any = (window as any)["Jed"];
|
||||||
var jed;
|
var jed: any;
|
||||||
|
|
||||||
|
|
||||||
class PluralNumber {
|
class PluralNumber {
|
||||||
n: number;
|
n: number;
|
||||||
|
|
||||||
constructor(n) {
|
constructor(n: number) {
|
||||||
this.n = n;
|
this.n = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ function init () {
|
|||||||
|
|
||||||
|
|
||||||
/** Convert template strings to a msgid */
|
/** Convert template strings to a msgid */
|
||||||
function toI18nString(strings) {
|
function toI18nString(strings: string[]) {
|
||||||
let str = "";
|
let str = "";
|
||||||
for (let i = 0; i < strings.length; i++) {
|
for (let i = 0; i < strings.length; i++) {
|
||||||
str += strings[i];
|
str += strings[i];
|
||||||
@ -75,7 +75,7 @@ function toI18nString(strings) {
|
|||||||
|
|
||||||
|
|
||||||
/** Use the first number in values to determine plural form */
|
/** Use the first number in values to determine plural form */
|
||||||
function getPluralValue (values) {
|
function getPluralValue (values: any) {
|
||||||
let n = null;
|
let n = null;
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
if ("number" === typeof values[i] || values[i] instanceof PluralNumber) {
|
if ("number" === typeof values[i] || values[i] instanceof PluralNumber) {
|
||||||
@ -88,11 +88,11 @@ function getPluralValue (values) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var i18n = <any>function i18n(strings, ...values) {
|
var i18n = <any>function i18n(strings: string[], ...values: any[]) {
|
||||||
init();
|
init();
|
||||||
if ("object" !== typeof jed) {
|
if ("object" !== typeof jed) {
|
||||||
// Fallback implementation in case i18n lib is not there
|
// Fallback implementation in case i18n lib is not there
|
||||||
return String.raw(strings, ...values);
|
return String.raw(strings as any, ...values);
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = toI18nString (strings);
|
let str = toI18nString (strings);
|
||||||
@ -109,11 +109,11 @@ i18n.strings = {};
|
|||||||
* Interpolate i18nized values with arbitrary objects.
|
* Interpolate i18nized values with arbitrary objects.
|
||||||
* @return Array of strings/objects.
|
* @return Array of strings/objects.
|
||||||
*/
|
*/
|
||||||
i18n.parts = function(strings, ...values) {
|
i18n.parts = function(strings: string[], ...values: any[]) {
|
||||||
init();
|
init();
|
||||||
if ("object" !== typeof jed) {
|
if ("object" !== typeof jed) {
|
||||||
// Fallback implementation in case i18n lib is not there
|
// Fallback implementation in case i18n lib is not there
|
||||||
let parts = [];
|
let parts: string[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < strings.length; i++) {
|
for (let i = 0; i < strings.length; i++) {
|
||||||
parts.push(strings[i]);
|
parts.push(strings[i]);
|
||||||
@ -127,7 +127,7 @@ i18n.parts = function(strings, ...values) {
|
|||||||
let str = toI18nString (strings);
|
let str = toI18nString (strings);
|
||||||
let n = getPluralValue (values);
|
let n = getPluralValue (values);
|
||||||
let tr = jed.ngettext(str, str, n).split(/%(\d+)\$s/);
|
let tr = jed.ngettext(str, str, n).split(/%(\d+)\$s/);
|
||||||
let parts = [];
|
let parts: string[] = [];
|
||||||
for (let i = 0; i < tr.length; i++) {
|
for (let i = 0; i < tr.length; i++) {
|
||||||
if (0 == i % 2) {
|
if (0 == i % 2) {
|
||||||
parts.push(tr[i]);
|
parts.push(tr[i]);
|
||||||
@ -144,7 +144,7 @@ i18n.parts = function(strings, ...values) {
|
|||||||
* Pluralize based on first numeric parameter in the template.
|
* Pluralize based on first numeric parameter in the template.
|
||||||
* @todo The plural argument is used for extraction by pogen.js
|
* @todo The plural argument is used for extraction by pogen.js
|
||||||
*/
|
*/
|
||||||
i18n.pluralize = function (singular, plural) {
|
i18n.pluralize = function (singular: any, plural: any) {
|
||||||
return singular;
|
return singular;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -154,4 +154,4 @@ i18n.pluralize = function (singular, plural) {
|
|||||||
*/
|
*/
|
||||||
i18n.number = function (n : number) {
|
i18n.number = function (n : number) {
|
||||||
return new PluralNumber (n);
|
return new PluralNumber (n);
|
||||||
}
|
};
|
||||||
|
169
lib/shopApi.ts
Normal file
169
lib/shopApi.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
(C) 2015 GNUnet e.V.
|
||||||
|
|
||||||
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
|
terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the shop API, either invoked via HTTP or
|
||||||
|
* via a JS DOM Events.
|
||||||
|
*
|
||||||
|
* @author Florian Dold
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function subst(url: string, H_contract: string) {
|
||||||
|
url = url.replace("${H_contract}", H_contract);
|
||||||
|
url = url.replace("${$}", "$");
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createReserve(amount: any, callback_url: any, wt_types: any) {
|
||||||
|
let params = {
|
||||||
|
amount: JSON.stringify(amount),
|
||||||
|
callback_url: URI(callback_url)
|
||||||
|
.absoluteTo(document.location.href),
|
||||||
|
bank_url: document.location.href,
|
||||||
|
wt_types: JSON.stringify(wt_types),
|
||||||
|
};
|
||||||
|
let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
|
||||||
|
document.location.href = uri.query(params).href();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function confirmContract(contract_wrapper: any, replace_navigation: any) {
|
||||||
|
if (contract_wrapper) {
|
||||||
|
console.error("contract wrapper missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offer = contract_wrapper;
|
||||||
|
|
||||||
|
if (!offer.contract) {
|
||||||
|
console.error("contract field missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
type: "check-repurchase",
|
||||||
|
detail: {
|
||||||
|
contract: offer.contract
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.runtime.sendMessage(msg, (resp) => {
|
||||||
|
if (resp.error) {
|
||||||
|
console.error("wallet backend error", resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (resp.isRepurchase) {
|
||||||
|
console.log("doing repurchase");
|
||||||
|
console.assert(resp.existingFulfillmentUrl);
|
||||||
|
console.assert(resp.existingContractHash);
|
||||||
|
window.location.href = subst(resp.existingFulfillmentUrl,
|
||||||
|
resp.existingContractHash);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
|
||||||
|
const params = {
|
||||||
|
offer: JSON.stringify(offer),
|
||||||
|
merchantPageUrl: document.location.href,
|
||||||
|
};
|
||||||
|
const target = uri.query(params).href();
|
||||||
|
if (replace_navigation === true) {
|
||||||
|
document.location.replace(target);
|
||||||
|
} else {
|
||||||
|
document.location.href = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a payment (coin deposit permissions) for a given contract.
|
||||||
|
* If we don't have the payment for the contract, redirect to
|
||||||
|
* offering url instead.
|
||||||
|
*/
|
||||||
|
export function fetchPayment(H_contract: any, offering_url: any) {
|
||||||
|
const msg = {
|
||||||
|
type: "fetch-payment",
|
||||||
|
detail: {H_contract},
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.runtime.sendMessage(msg, (resp) => {
|
||||||
|
console.log("got resp");
|
||||||
|
console.dir(resp);
|
||||||
|
if (!resp.success) {
|
||||||
|
if (offering_url) {
|
||||||
|
console.log("offering url", offering_url);
|
||||||
|
window.location.href = offering_url;
|
||||||
|
} else {
|
||||||
|
console.error("fetch-payment failed");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let contract = resp.contract;
|
||||||
|
if (!contract) {
|
||||||
|
throw Error("contract missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have the details for then payment, the merchant page
|
||||||
|
// is responsible to give it to the merchant.
|
||||||
|
|
||||||
|
let evt = new CustomEvent("taler-notify-payment", {
|
||||||
|
detail: {
|
||||||
|
H_contract: H_contract,
|
||||||
|
contract: resp.contract,
|
||||||
|
payment: resp.payReq,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.dispatchEvent(evt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer a contract to the wallet after
|
||||||
|
* downloading it from the given URL.
|
||||||
|
*/
|
||||||
|
function offerContractFrom(url: string) {
|
||||||
|
var contract_request = new XMLHttpRequest();
|
||||||
|
console.log("downloading contract from '" + url + "'");
|
||||||
|
contract_request.open("GET", url, true);
|
||||||
|
contract_request.onload = function (e) {
|
||||||
|
if (contract_request.readyState == 4) {
|
||||||
|
if (contract_request.status == 200) {
|
||||||
|
console.log("response text:",
|
||||||
|
contract_request.responseText);
|
||||||
|
var contract_wrapper = JSON.parse(contract_request.responseText);
|
||||||
|
if (!contract_wrapper) {
|
||||||
|
console.error("response text was invalid json");
|
||||||
|
alert("Failure to download contract (invalid json)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
confirmContract(contract_wrapper, true);
|
||||||
|
} else {
|
||||||
|
alert("Failure to download contract from merchant " +
|
||||||
|
"(" + contract_request.status + "):\n" +
|
||||||
|
contract_request.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
contract_request.onerror = function (e) {
|
||||||
|
alert("Failure requesting the contract:\n"
|
||||||
|
+ contract_request.statusText);
|
||||||
|
};
|
||||||
|
contract_request.send();
|
||||||
|
}
|
4390
lib/vendor/mithril.js
vendored
4390
lib/vendor/mithril.js
vendored
File diff suppressed because it is too large
Load Diff
@ -25,18 +25,39 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export namespace Checkable {
|
export namespace Checkable {
|
||||||
export function SchemaError(message) {
|
|
||||||
|
type Path = (number|string)[];
|
||||||
|
|
||||||
|
interface SchemaErrorConstructor {
|
||||||
|
new (err: string): SchemaError;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SchemaError {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Prop {
|
||||||
|
propertyKey: any;
|
||||||
|
checker: any;
|
||||||
|
type: any;
|
||||||
|
elementChecker?: any;
|
||||||
|
elementProp?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let SchemaError = (function SchemaError(message: string) {
|
||||||
this.name = 'SchemaError';
|
this.name = 'SchemaError';
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.stack = (<any>new Error()).stack;
|
this.stack = (<any>new Error()).stack;
|
||||||
}
|
}) as any as SchemaErrorConstructor;
|
||||||
|
|
||||||
|
|
||||||
SchemaError.prototype = new Error;
|
SchemaError.prototype = new Error;
|
||||||
|
|
||||||
let chkSym = Symbol("checkable");
|
let chkSym = Symbol("checkable");
|
||||||
|
|
||||||
|
|
||||||
function checkNumber(target, prop, path): any {
|
function checkNumber(target: any, prop: Prop, path: Path): any {
|
||||||
if ((typeof target) !== "number") {
|
if ((typeof target) !== "number") {
|
||||||
throw new SchemaError(`expected number for ${path}`);
|
throw new SchemaError(`expected number for ${path}`);
|
||||||
}
|
}
|
||||||
@ -44,7 +65,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkString(target, prop, path): any {
|
function checkString(target: any, prop: Prop, path: Path): any {
|
||||||
if (typeof target !== "string") {
|
if (typeof target !== "string") {
|
||||||
throw new SchemaError(`expected string for ${path}, got ${typeof target} instead`);
|
throw new SchemaError(`expected string for ${path}, got ${typeof target} instead`);
|
||||||
}
|
}
|
||||||
@ -52,7 +73,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkAnyObject(target, prop, path): any {
|
function checkAnyObject(target: any, prop: Prop, path: Path): any {
|
||||||
if (typeof target !== "object") {
|
if (typeof target !== "object") {
|
||||||
throw new SchemaError(`expected (any) object for ${path}, got ${typeof target} instead`);
|
throw new SchemaError(`expected (any) object for ${path}, got ${typeof target} instead`);
|
||||||
}
|
}
|
||||||
@ -60,12 +81,12 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkAny(target, prop, path): any {
|
function checkAny(target: any, prop: Prop, path: Path): any {
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkList(target, prop, path): any {
|
function checkList(target: any, prop: Prop, path: Path): any {
|
||||||
if (!Array.isArray(target)) {
|
if (!Array.isArray(target)) {
|
||||||
throw new SchemaError(`array expected for ${path}, got ${typeof target} instead`);
|
throw new SchemaError(`array expected for ${path}, got ${typeof target} instead`);
|
||||||
}
|
}
|
||||||
@ -77,7 +98,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkOptional(target, prop, path): any {
|
function checkOptional(target: any, prop: Prop, path: Path): any {
|
||||||
console.assert(prop.propertyKey);
|
console.assert(prop.propertyKey);
|
||||||
prop.elementChecker(target,
|
prop.elementChecker(target,
|
||||||
prop.elementProp,
|
prop.elementProp,
|
||||||
@ -86,7 +107,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkValue(target, prop, path): any {
|
function checkValue(target: any, prop: Prop, path: Path): any {
|
||||||
let type = prop.type;
|
let type = prop.type;
|
||||||
if (!type) {
|
if (!type) {
|
||||||
throw Error(`assertion failed (prop is ${JSON.stringify(prop)})`);
|
throw Error(`assertion failed (prop is ${JSON.stringify(prop)})`);
|
||||||
@ -123,8 +144,8 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Class(target) {
|
export function Class(target: any) {
|
||||||
target.checked = (v) => {
|
target.checked = (v: any) => {
|
||||||
return checkValue(v, {
|
return checkValue(v, {
|
||||||
propertyKey: "(root)",
|
propertyKey: "(root)",
|
||||||
type: target,
|
type: target,
|
||||||
@ -135,7 +156,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Value(type) {
|
export function Value(type: any) {
|
||||||
if (!type) {
|
if (!type) {
|
||||||
throw Error("Type does not exist yet (wrong order of definitions?)");
|
throw Error("Type does not exist yet (wrong order of definitions?)");
|
||||||
}
|
}
|
||||||
@ -152,7 +173,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function List(type) {
|
export function List(type: any) {
|
||||||
let stub = {};
|
let stub = {};
|
||||||
type(stub, "(list-element)");
|
type(stub, "(list-element)");
|
||||||
let elementProp = mkChk(stub).props[0];
|
let elementProp = mkChk(stub).props[0];
|
||||||
@ -174,7 +195,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Optional(type) {
|
export function Optional(type: any) {
|
||||||
let stub = {};
|
let stub = {};
|
||||||
type(stub, "(optional-element)");
|
type(stub, "(optional-element)");
|
||||||
let elementProp = mkChk(stub).props[0];
|
let elementProp = mkChk(stub).props[0];
|
||||||
@ -230,7 +251,7 @@ export namespace Checkable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mkChk(target) {
|
function mkChk(target: any) {
|
||||||
let chk = target[chkSym];
|
let chk = target[chkSym];
|
||||||
if (!chk) {
|
if (!chk) {
|
||||||
chk = {props: []};
|
chk = {props: []};
|
||||||
|
@ -27,9 +27,10 @@ import {Denomination} from "./types";
|
|||||||
import {Offer} from "./wallet";
|
import {Offer} from "./wallet";
|
||||||
import {CoinWithDenom} from "./wallet";
|
import {CoinWithDenom} from "./wallet";
|
||||||
import {PayCoinInfo} from "./types";
|
import {PayCoinInfo} from "./types";
|
||||||
|
type RegistryEntry = {resolve: any; reject: any};
|
||||||
export class CryptoApi {
|
export class CryptoApi {
|
||||||
private nextRpcId: number = 1;
|
private nextRpcId: number = 1;
|
||||||
private rpcRegistry = {};
|
private rpcRegistry: {[n: number]: RegistryEntry} = {};
|
||||||
private cryptoWorker: Worker;
|
private cryptoWorker: Worker;
|
||||||
|
|
||||||
|
|
||||||
@ -52,14 +53,14 @@ export class CryptoApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private registerRpcId(resolve, reject): number {
|
private registerRpcId(resolve: any, reject: any): number {
|
||||||
let id = this.nextRpcId++;
|
let id = this.nextRpcId++;
|
||||||
this.rpcRegistry[id] = {resolve, reject};
|
this.rpcRegistry[id] = {resolve, reject};
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private doRpc<T>(methodName: string, ...args): Promise<T> {
|
private doRpc<T>(methodName: string, ...args: any[]): Promise<T> {
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
let msg = {
|
let msg = {
|
||||||
operation: methodName,
|
operation: methodName,
|
||||||
|
@ -28,6 +28,7 @@ import {Offer} from "./wallet";
|
|||||||
import {CoinWithDenom} from "./wallet";
|
import {CoinWithDenom} from "./wallet";
|
||||||
import {CoinPaySig} from "./types";
|
import {CoinPaySig} from "./types";
|
||||||
import {Denomination} from "./types";
|
import {Denomination} from "./types";
|
||||||
|
import {Amount} from "./emscriptif";
|
||||||
|
|
||||||
|
|
||||||
export function main(worker: Worker) {
|
export function main(worker: Worker) {
|
||||||
@ -43,7 +44,7 @@ export function main(worker: Worker) {
|
|||||||
if (typeof msg.data.operation != "string") {
|
if (typeof msg.data.operation != "string") {
|
||||||
console.error("RPC operation must be string");
|
console.error("RPC operation must be string");
|
||||||
}
|
}
|
||||||
let f = RpcFunctions[msg.data.operation];
|
let f = (RpcFunctions as any)[msg.data.operation];
|
||||||
if (!f) {
|
if (!f) {
|
||||||
console.error(`unknown operation: '${msg.data.operation}'`);
|
console.error(`unknown operation: '${msg.data.operation}'`);
|
||||||
return;
|
return;
|
||||||
@ -156,7 +157,7 @@ namespace RpcFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function rsaUnblind(sig, bk, pk): string {
|
export function rsaUnblind(sig: string, bk: string, pk: string): string {
|
||||||
let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
|
let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
|
||||||
native.RsaBlindingKeySecret.fromCrock(bk),
|
native.RsaBlindingKeySecret.fromCrock(bk),
|
||||||
native.RsaPublicKey.fromCrock(pk));
|
native.RsaPublicKey.fromCrock(pk));
|
||||||
@ -170,11 +171,11 @@ namespace RpcFunctions {
|
|||||||
*/
|
*/
|
||||||
export function signDeposit(offer: Offer,
|
export function signDeposit(offer: Offer,
|
||||||
cds: CoinWithDenom[]): PayCoinInfo {
|
cds: CoinWithDenom[]): PayCoinInfo {
|
||||||
let ret = [];
|
let ret: PayCoinInfo = [];
|
||||||
let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);
|
let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);
|
||||||
let amountRemaining = new native.Amount(offer.contract.amount);
|
let amountRemaining = new native.Amount(offer.contract.amount);
|
||||||
for (let cd of cds) {
|
for (let cd of cds) {
|
||||||
let coinSpend;
|
let coinSpend: Amount;
|
||||||
|
|
||||||
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
|
if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
import Dictionary = _.Dictionary;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declarations and helpers for
|
* Declarations and helpers for
|
||||||
@ -83,27 +84,27 @@ export function openTalerDb(): Promise<IDBDatabase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function exportDb(db): Promise<any> {
|
export function exportDb(db: IDBDatabase): Promise<any> {
|
||||||
let dump = {
|
let dump = {
|
||||||
name: db.name,
|
name: db.name,
|
||||||
version: db.version,
|
version: db.version,
|
||||||
stores: {}
|
stores: {} as Dictionary<any>,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
let tx = db.transaction(db.objectStoreNames);
|
let tx = db.transaction(Array.from(db.objectStoreNames));
|
||||||
tx.addEventListener("complete", (e) => {
|
tx.addEventListener("complete", () => {
|
||||||
resolve(dump);
|
resolve(dump);
|
||||||
});
|
});
|
||||||
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
||||||
let name = db.objectStoreNames[i];
|
let name = db.objectStoreNames[i];
|
||||||
let storeDump = {};
|
let storeDump = {} as Dictionary<any>;
|
||||||
dump.stores[name] = storeDump;
|
dump.stores[name] = storeDump;
|
||||||
let store = tx.objectStore(name)
|
let store = tx.objectStore(name)
|
||||||
.openCursor()
|
.openCursor()
|
||||||
.addEventListener("success", (e) => {
|
.addEventListener("success", (e: Event) => {
|
||||||
let cursor = e.target.result;
|
let cursor = (e.target as any).result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
storeDump[cursor.key] = cursor.value;
|
storeDump[cursor.key] = cursor.value;
|
||||||
cursor.continue();
|
cursor.continue();
|
||||||
|
@ -36,11 +36,11 @@ const GNUNET_SYSERR = -1;
|
|||||||
|
|
||||||
let Module = EmscWrapper.Module;
|
let Module = EmscWrapper.Module;
|
||||||
|
|
||||||
let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null,
|
let getEmsc: EmscWrapper.EmscFunGen = (...args: any[]) => Module.cwrap.apply(null,
|
||||||
args);
|
args);
|
||||||
|
|
||||||
var emsc = {
|
var emsc = {
|
||||||
free: (ptr) => Module._free(ptr),
|
free: (ptr: number) => Module._free(ptr),
|
||||||
get_value: getEmsc('TALER_WR_get_value',
|
get_value: getEmsc('TALER_WR_get_value',
|
||||||
'number',
|
'number',
|
||||||
['number']),
|
['number']),
|
||||||
@ -164,13 +164,12 @@ enum RandomQuality {
|
|||||||
|
|
||||||
|
|
||||||
abstract class ArenaObject {
|
abstract class ArenaObject {
|
||||||
private _nativePtr: number;
|
protected _nativePtr: number | undefined = undefined;
|
||||||
arena: Arena;
|
arena: Arena;
|
||||||
|
|
||||||
abstract destroy(): void;
|
abstract destroy(): void;
|
||||||
|
|
||||||
constructor(arena?: Arena) {
|
constructor(arena?: Arena) {
|
||||||
this.nativePtr = null;
|
|
||||||
if (!arena) {
|
if (!arena) {
|
||||||
if (arenaStack.length == 0) {
|
if (arenaStack.length == 0) {
|
||||||
throw Error("No arena available")
|
throw Error("No arena available")
|
||||||
@ -192,14 +191,14 @@ abstract class ArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free() {
|
free() {
|
||||||
if (this.nativePtr !== undefined) {
|
if (this.nativePtr) {
|
||||||
emsc.free(this.nativePtr);
|
emsc.free(this.nativePtr);
|
||||||
this.nativePtr = undefined;
|
this._nativePtr = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc(size: number) {
|
alloc(size: number) {
|
||||||
if (this.nativePtr !== undefined) {
|
if (this._nativePtr !== undefined) {
|
||||||
throw Error("Double allocation");
|
throw Error("Double allocation");
|
||||||
}
|
}
|
||||||
this.nativePtr = emscAlloc.malloc(size);
|
this.nativePtr = emscAlloc.malloc(size);
|
||||||
@ -212,21 +211,22 @@ abstract class ArenaObject {
|
|||||||
this._nativePtr = n;
|
this._nativePtr = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
set nativePtr(v) {
|
set nativePtr(v: number) {
|
||||||
this.setNative(v);
|
this.setNative(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
get nativePtr() {
|
get nativePtr() {
|
||||||
return this.getNative();
|
return this.getNative();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Arena {
|
interface Arena {
|
||||||
put(obj: ArenaObject): void;
|
put(obj: ArenaObject): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DefaultArena implements Arena {
|
class DefaultArena implements Arena {
|
||||||
heap: Array<ArenaObject>;
|
heap: Array<ArenaObject>;
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ class DefaultArena implements Arena {
|
|||||||
this.heap = [];
|
this.heap = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
put(obj) {
|
put(obj: ArenaObject) {
|
||||||
this.heap.push(obj);
|
this.heap.push(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +269,7 @@ class SyncArena extends DefaultArena {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(obj) {
|
pub(obj: ArenaObject) {
|
||||||
super.put(obj);
|
super.put(obj);
|
||||||
if (!this.isScheduled) {
|
if (!this.isScheduled) {
|
||||||
this.schedule();
|
this.schedule();
|
||||||
@ -308,14 +308,12 @@ export class Amount extends ArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.nativePtr != 0) {
|
super.free();
|
||||||
emsc.free(this.nativePtr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static getZero(currency: string, a?: Arena): Amount {
|
static getZero(currency: string, a?: Arena): Amount {
|
||||||
let am = new Amount(null, a);
|
let am = new Amount(undefined, a);
|
||||||
let r = emsc.amount_get_zero(currency, am.getNative());
|
let r = emsc.amount_get_zero(currency, am.getNative());
|
||||||
if (r != GNUNET_OK) {
|
if (r != GNUNET_OK) {
|
||||||
throw Error("invalid currency");
|
throw Error("invalid currency");
|
||||||
@ -442,7 +440,8 @@ abstract class PackedArenaObject extends ArenaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alloc() {
|
alloc() {
|
||||||
if (this.nativePtr === null) {
|
// FIXME: should the client be allowed to call alloc multiple times?
|
||||||
|
if (!this._nativePtr) {
|
||||||
this.nativePtr = emscAlloc.malloc(this.size());
|
this.nativePtr = emscAlloc.malloc(this.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,7 +465,7 @@ abstract class PackedArenaObject extends ArenaObject {
|
|||||||
b = (b + 256) % 256;
|
b = (b + 256) % 256;
|
||||||
bytes.push("0".concat(b.toString(16)).slice(-2));
|
bytes.push("0".concat(b.toString(16)).slice(-2));
|
||||||
}
|
}
|
||||||
let lines = [];
|
let lines: string[] = [];
|
||||||
for (let i = 0; i < bytes.length; i += 8) {
|
for (let i = 0; i < bytes.length; i += 8) {
|
||||||
lines.push(bytes.slice(i, i + 8).join(","));
|
lines.push(bytes.slice(i, i + 8).join(","));
|
||||||
}
|
}
|
||||||
@ -482,7 +481,7 @@ export class AmountNbo extends PackedArenaObject {
|
|||||||
|
|
||||||
toJson(): any {
|
toJson(): any {
|
||||||
let a = new DefaultArena();
|
let a = new DefaultArena();
|
||||||
let am = new Amount(null, a);
|
let am = new Amount(undefined, a);
|
||||||
am.fromNbo(this);
|
am.fromNbo(this);
|
||||||
let json = am.toJson();
|
let json = am.toJson();
|
||||||
a.destroy();
|
a.destroy();
|
||||||
@ -508,7 +507,7 @@ export class EddsaPrivateKey extends PackedArenaObject {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock: (string) => EddsaPrivateKey;
|
static fromCrock: (s: string) => EddsaPrivateKey;
|
||||||
}
|
}
|
||||||
mixinStatic(EddsaPrivateKey, fromCrock);
|
mixinStatic(EddsaPrivateKey, fromCrock);
|
||||||
|
|
||||||
@ -521,7 +520,7 @@ function fromCrock(s: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mixin(obj, method, name?: string) {
|
function mixin(obj: any, method: any, name?: string) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
name = method.name;
|
name = method.name;
|
||||||
}
|
}
|
||||||
@ -532,7 +531,7 @@ function mixin(obj, method, name?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mixinStatic(obj, method, name?: string) {
|
function mixinStatic(obj: any, method: any, name?: string) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
name = method.name;
|
name = method.name;
|
||||||
}
|
}
|
||||||
@ -595,7 +594,7 @@ export class RsaBlindingKeySecret extends PackedArenaObject {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCrock: (string) => RsaBlindingKeySecret;
|
static fromCrock: (s: string) => RsaBlindingKeySecret;
|
||||||
}
|
}
|
||||||
mixinStatic(RsaBlindingKeySecret, fromCrock);
|
mixinStatic(RsaBlindingKeySecret, fromCrock);
|
||||||
|
|
||||||
@ -622,9 +621,9 @@ export class ByteArray extends PackedArenaObject {
|
|||||||
return this.allocatedSize;
|
return this.allocatedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(desiredSize: number, init: number, a?: Arena) {
|
constructor(desiredSize: number, init?: number, a?: Arena) {
|
||||||
super(a);
|
super(a);
|
||||||
if (init === undefined || init === null) {
|
if (init === undefined) {
|
||||||
this.nativePtr = emscAlloc.malloc(desiredSize);
|
this.nativePtr = emscAlloc.malloc(desiredSize);
|
||||||
} else {
|
} else {
|
||||||
this.nativePtr = init;
|
this.nativePtr = init;
|
||||||
@ -642,7 +641,7 @@ export class ByteArray extends PackedArenaObject {
|
|||||||
let hstr = emscAlloc.malloc(s.length + 1);
|
let hstr = emscAlloc.malloc(s.length + 1);
|
||||||
Module.writeStringToMemory(s, hstr);
|
Module.writeStringToMemory(s, hstr);
|
||||||
let decodedLen = Math.floor((s.length * 5) / 8);
|
let decodedLen = Math.floor((s.length * 5) / 8);
|
||||||
let ba = new ByteArray(decodedLen, null, a);
|
let ba = new ByteArray(decodedLen, undefined, a);
|
||||||
let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
|
let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
|
||||||
emsc.free(hstr);
|
emsc.free(hstr);
|
||||||
if (res != GNUNET_OK) {
|
if (res != GNUNET_OK) {
|
||||||
@ -777,7 +776,7 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
|
|||||||
x.alloc();
|
x.alloc();
|
||||||
let r = /Date\(([0-9]+)\)/;
|
let r = /Date\(([0-9]+)\)/;
|
||||||
let m = r.exec(s);
|
let m = r.exec(s);
|
||||||
if (m.length != 2) {
|
if (!m || m.length != 2) {
|
||||||
throw Error();
|
throw Error();
|
||||||
}
|
}
|
||||||
let n = parseInt(m[1]) * 1000000;
|
let n = parseInt(m[1]) * 1000000;
|
||||||
@ -899,11 +898,11 @@ interface Encodeable {
|
|||||||
encode(arena?: Arena): ByteArray;
|
encode(arena?: Arena): ByteArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeEncode(encodeFn) {
|
function makeEncode(encodeFn: any) {
|
||||||
function encode(arena?: Arena) {
|
function encode(arena?: Arena) {
|
||||||
let ptr = emscAlloc.malloc(PTR_SIZE);
|
let ptr = emscAlloc.malloc(PTR_SIZE);
|
||||||
let len = encodeFn(this.getNative(), ptr);
|
let len = encodeFn(this.getNative(), ptr);
|
||||||
let res = new ByteArray(len, null, arena);
|
let res = new ByteArray(len, undefined, arena);
|
||||||
res.setNative(Module.getValue(ptr, '*'));
|
res.setNative(Module.getValue(ptr, '*'));
|
||||||
emsc.free(ptr);
|
emsc.free(ptr);
|
||||||
return res;
|
return res;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
import {AmountJson} from "./types";
|
import {AmountJson} from "./types";
|
||||||
|
|
||||||
export function substituteFulfillmentUrl(url: string, vars) {
|
export function substituteFulfillmentUrl(url: string, vars: any) {
|
||||||
url = url.replace("${H_contract}", vars.H_contract);
|
url = url.replace("${H_contract}", vars.H_contract);
|
||||||
url = url.replace("${$}", "$");
|
url = url.replace("${$}", "$");
|
||||||
return url;
|
return url;
|
||||||
@ -42,7 +42,7 @@ export function amountToPretty(amount: AmountJson): string {
|
|||||||
*
|
*
|
||||||
* See http://api.taler.net/wallet.html#general
|
* See http://api.taler.net/wallet.html#general
|
||||||
*/
|
*/
|
||||||
export function canonicalizeBaseUrl(url) {
|
export function canonicalizeBaseUrl(url: string) {
|
||||||
let x = new URI(url);
|
let x = new URI(url);
|
||||||
if (!x.protocol()) {
|
if (!x.protocol()) {
|
||||||
x.protocol("https");
|
x.protocol("https");
|
||||||
@ -54,10 +54,10 @@ export function canonicalizeBaseUrl(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function parsePrettyAmount(pretty: string): AmountJson {
|
export function parsePrettyAmount(pretty: string): AmountJson|undefined {
|
||||||
const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
|
const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
value: parseInt(res[1], 10),
|
value: parseInt(res[1], 10),
|
||||||
|
@ -66,19 +66,19 @@ export class BrowserHttpLib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
postJson(url: string|uri.URI, body) {
|
postJson(url: string|uri.URI, body: any) {
|
||||||
return this.req("post", url, {req: JSON.stringify(body)});
|
return this.req("post", url, {req: JSON.stringify(body)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
postForm(url: string|uri.URI, form) {
|
postForm(url: string|uri.URI, form: any) {
|
||||||
return this.req("post", url, {req: form});
|
return this.req("post", url, {req: form});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class RequestException {
|
export class RequestException {
|
||||||
constructor(detail) {
|
constructor(detail: any) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
export function Query(db) {
|
export function Query(db: IDBDatabase) {
|
||||||
return new QueryRoot(db);
|
return new QueryRoot(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,24 +36,27 @@ export interface QueryStream<T> {
|
|||||||
indexJoin<S>(storeName: string,
|
indexJoin<S>(storeName: string,
|
||||||
indexName: string,
|
indexName: string,
|
||||||
keyFn: (obj: any) => any): QueryStream<[T,S]>;
|
keyFn: (obj: any) => any): QueryStream<[T,S]>;
|
||||||
filter(f: (any) => boolean): QueryStream<T>;
|
filter(f: (x: any) => boolean): QueryStream<T>;
|
||||||
reduce<S>(f: (v: T, acc: S) => S, start?: S): Promise<S>;
|
reduce<S>(f: (v: T, acc: S) => S, start?: S): Promise<S>;
|
||||||
flatMap(f: (T) => T[]): QueryStream<T>;
|
flatMap(f: (x: T) => T[]): QueryStream<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an unresolved promise together with its extracted resolve / reject
|
* Get an unresolved promise together with its extracted resolve / reject
|
||||||
* function.
|
* function.
|
||||||
*
|
|
||||||
* @returns {{resolve: any, reject: any, promise: Promise<T>}}
|
|
||||||
*/
|
*/
|
||||||
function openPromise<T>() {
|
function openPromise<T>() {
|
||||||
let resolve, reject;
|
let resolve: ((value?: T | PromiseLike<T>) => void) | null = null;
|
||||||
|
let reject: ((reason?: any) => void) | null = null;
|
||||||
const promise = new Promise<T>((res, rej) => {
|
const promise = new Promise<T>((res, rej) => {
|
||||||
resolve = res;
|
resolve = res;
|
||||||
reject = rej;
|
reject = rej;
|
||||||
});
|
});
|
||||||
|
if (!(resolve && reject)) {
|
||||||
|
// Never happens, unless JS implementation is broken
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
return {resolve, reject, promise};
|
return {resolve, reject, promise};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +64,7 @@ function openPromise<T>() {
|
|||||||
abstract class QueryStreamBase<T> implements QueryStream<T> {
|
abstract class QueryStreamBase<T> implements QueryStream<T> {
|
||||||
abstract subscribe(f: (isDone: boolean,
|
abstract subscribe(f: (isDone: boolean,
|
||||||
value: any,
|
value: any,
|
||||||
tx: IDBTransaction) => void);
|
tx: IDBTransaction) => void): void;
|
||||||
|
|
||||||
root: QueryRoot;
|
root: QueryRoot;
|
||||||
|
|
||||||
@ -69,30 +72,28 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
|
|||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
flatMap(f: (T) => T[]): QueryStream<T> {
|
flatMap(f: (x: T) => T[]): QueryStream<T> {
|
||||||
return new QueryStreamFlatMap(this, f);
|
return new QueryStreamFlatMap(this, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexJoin<S>(storeName: string,
|
indexJoin<S>(storeName: string,
|
||||||
indexName: string,
|
indexName: string,
|
||||||
key: any): QueryStream<[T,S]> {
|
key: any): QueryStream<[T,S]> {
|
||||||
this.root.addWork(null, storeName, false);
|
this.root.addStoreAccess(storeName, false);
|
||||||
return new QueryStreamIndexJoin(this, storeName, indexName, key);
|
return new QueryStreamIndexJoin(this, storeName, indexName, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
filter(f: (any) => boolean): QueryStream<T> {
|
filter(f: (x: any) => boolean): QueryStream<T> {
|
||||||
return new QueryStreamFilter(this, f);
|
return new QueryStreamFilter(this, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
reduce(f, acc?): Promise<any> {
|
reduce<A>(f: (x: any, acc?: A) => A, init?: A): Promise<any> {
|
||||||
let leakedResolve;
|
let {resolve, promise} = openPromise();
|
||||||
let p = new Promise((resolve, reject) => {
|
let acc = init;
|
||||||
leakedResolve = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.subscribe((isDone, value) => {
|
this.subscribe((isDone, value) => {
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
leakedResolve(acc);
|
resolve(acc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acc = f(value, acc);
|
acc = f(value, acc);
|
||||||
@ -100,22 +101,28 @@ abstract class QueryStreamBase<T> implements QueryStream<T> {
|
|||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => this.root.finish())
|
.then(() => this.root.finish())
|
||||||
.then(() => p);
|
.then(() => promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilterFn = (e: any) => boolean;
|
||||||
|
type SubscribeFn = (done: boolean, value: any, tx: IDBTransaction) => void;
|
||||||
|
|
||||||
|
interface FlatMapFn<T> {
|
||||||
|
(v: T): T[];
|
||||||
|
}
|
||||||
|
|
||||||
class QueryStreamFilter<T> extends QueryStreamBase<T> {
|
class QueryStreamFilter<T> extends QueryStreamBase<T> {
|
||||||
s: QueryStreamBase<T>;
|
s: QueryStreamBase<T>;
|
||||||
filterFn;
|
filterFn: FilterFn;
|
||||||
|
|
||||||
constructor(s: QueryStreamBase<T>, filterFn) {
|
constructor(s: QueryStreamBase<T>, filterFn: FilterFn) {
|
||||||
super(s.root);
|
super(s.root);
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.filterFn = filterFn;
|
this.filterFn = filterFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(f) {
|
subscribe(f: SubscribeFn) {
|
||||||
this.s.subscribe((isDone, value, tx) => {
|
this.s.subscribe((isDone, value, tx) => {
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
f(true, undefined, tx);
|
f(true, undefined, tx);
|
||||||
@ -131,15 +138,15 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
|
|||||||
|
|
||||||
class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
|
class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
|
||||||
s: QueryStreamBase<T>;
|
s: QueryStreamBase<T>;
|
||||||
flatMapFn;
|
flatMapFn: (v: T) => T[];
|
||||||
|
|
||||||
constructor(s: QueryStreamBase<T>, flatMapFn) {
|
constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => T[]) {
|
||||||
super(s.root);
|
super(s.root);
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.flatMap = flatMapFn;
|
this.flatMapFn = flatMapFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(f) {
|
subscribe(f: SubscribeFn) {
|
||||||
this.s.subscribe((isDone, value, tx) => {
|
this.s.subscribe((isDone, value, tx) => {
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
f(true, undefined, tx);
|
f(true, undefined, tx);
|
||||||
@ -154,13 +161,13 @@ class QueryStreamFlatMap<T> extends QueryStreamBase<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
|
class QueryStreamIndexJoin<T,S> extends QueryStreamBase<[T, S]> {
|
||||||
s: QueryStreamBase<T>;
|
s: QueryStreamBase<T>;
|
||||||
storeName;
|
storeName: string;
|
||||||
key;
|
key: any;
|
||||||
indexName;
|
indexName: string;
|
||||||
|
|
||||||
constructor(s, storeName: string, indexName: string, key: any) {
|
constructor(s: QueryStreamBase<T>, storeName: string, indexName: string, key: any) {
|
||||||
super(s.root);
|
super(s.root);
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.storeName = storeName;
|
this.storeName = storeName;
|
||||||
@ -168,7 +175,7 @@ class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
|
|||||||
this.indexName = indexName;
|
this.indexName = indexName;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(f) {
|
subscribe(f: SubscribeFn) {
|
||||||
this.s.subscribe((isDone, value, tx) => {
|
this.s.subscribe((isDone, value, tx) => {
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
f(true, undefined, tx);
|
f(true, undefined, tx);
|
||||||
@ -192,31 +199,31 @@ class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
|
|||||||
|
|
||||||
|
|
||||||
class IterQueryStream<T> extends QueryStreamBase<T> {
|
class IterQueryStream<T> extends QueryStreamBase<T> {
|
||||||
private storeName;
|
private storeName: string;
|
||||||
private options;
|
private options: any;
|
||||||
private subscribers;
|
private subscribers: SubscribeFn[];
|
||||||
|
|
||||||
constructor(qr, storeName, options) {
|
constructor(qr: QueryRoot, storeName: string, options: any) {
|
||||||
super(qr);
|
super(qr);
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.storeName = storeName;
|
this.storeName = storeName;
|
||||||
this.subscribers = [];
|
this.subscribers = [];
|
||||||
|
|
||||||
let doIt = (tx) => {
|
let doIt = (tx: IDBTransaction) => {
|
||||||
const {indexName = void 0, only = void 0} = this.options;
|
const {indexName = void 0, only = void 0} = this.options;
|
||||||
let s;
|
let s: any;
|
||||||
if (indexName !== void 0) {
|
if (indexName !== void 0) {
|
||||||
s = tx.objectStore(this.storeName)
|
s = tx.objectStore(this.storeName)
|
||||||
.index(this.options.indexName);
|
.index(this.options.indexName);
|
||||||
} else {
|
} else {
|
||||||
s = tx.objectStore(this.storeName);
|
s = tx.objectStore(this.storeName);
|
||||||
}
|
}
|
||||||
let kr = undefined;
|
let kr: IDBKeyRange|undefined = undefined;
|
||||||
if (only !== void 0) {
|
if (only !== undefined) {
|
||||||
kr = IDBKeyRange.only(this.options.only);
|
kr = IDBKeyRange.only(this.options.only);
|
||||||
}
|
}
|
||||||
let req = s.openCursor(kr);
|
let req = s.openCursor(kr);
|
||||||
req.onsuccess = (e) => {
|
req.onsuccess = () => {
|
||||||
let cursor: IDBCursorWithValue = req.result;
|
let cursor: IDBCursorWithValue = req.result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
for (let f of this.subscribers) {
|
for (let f of this.subscribers) {
|
||||||
@ -231,32 +238,33 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.root.addWork(doIt, null, false);
|
this.root.addWork(doIt);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(f) {
|
subscribe(f: SubscribeFn) {
|
||||||
this.subscribers.push(f);
|
this.subscribers.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class QueryRoot {
|
class QueryRoot {
|
||||||
private work = [];
|
private work: ((t: IDBTransaction) => void)[] = [];
|
||||||
private db: IDBDatabase;
|
private db: IDBDatabase;
|
||||||
private stores = new Set();
|
private stores = new Set();
|
||||||
private kickoffPromise;
|
private kickoffPromise: Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some operations is a write operation,
|
* Some operations is a write operation,
|
||||||
* and we need to do a "readwrite" transaction/
|
* and we need to do a "readwrite" transaction/
|
||||||
*/
|
*/
|
||||||
private hasWrite;
|
private hasWrite: boolean;
|
||||||
|
|
||||||
constructor(db) {
|
constructor(db: IDBDatabase) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
iter<T>(storeName, {only = void 0, indexName = void 0} = {}): QueryStream<T> {
|
iter<T>(storeName: string,
|
||||||
|
{only = <string|undefined>undefined, indexName = <string|undefined>undefined} = {}): QueryStream<T> {
|
||||||
this.stores.add(storeName);
|
this.stores.add(storeName);
|
||||||
return new IterQueryStream(this, storeName, {only, indexName});
|
return new IterQueryStream(this, storeName, {only, indexName});
|
||||||
}
|
}
|
||||||
@ -266,7 +274,7 @@ class QueryRoot {
|
|||||||
* Overrides if an existing object with the same key exists
|
* Overrides if an existing object with the same key exists
|
||||||
* in the store.
|
* in the store.
|
||||||
*/
|
*/
|
||||||
put(storeName, val): QueryRoot {
|
put(storeName: string, val: any): QueryRoot {
|
||||||
let doPut = (tx: IDBTransaction) => {
|
let doPut = (tx: IDBTransaction) => {
|
||||||
tx.objectStore(storeName).put(val);
|
tx.objectStore(storeName).put(val);
|
||||||
};
|
};
|
||||||
@ -280,7 +288,7 @@ class QueryRoot {
|
|||||||
* Fails if the object's key is already present
|
* Fails if the object's key is already present
|
||||||
* in the object store.
|
* in the object store.
|
||||||
*/
|
*/
|
||||||
putAll(storeName, iterable): QueryRoot {
|
putAll(storeName: string, iterable: any[]): QueryRoot {
|
||||||
const doPutAll = (tx: IDBTransaction) => {
|
const doPutAll = (tx: IDBTransaction) => {
|
||||||
for (const obj of iterable) {
|
for (const obj of iterable) {
|
||||||
tx.objectStore(storeName).put(obj);
|
tx.objectStore(storeName).put(obj);
|
||||||
@ -295,7 +303,7 @@ class QueryRoot {
|
|||||||
* Fails if the object's key is already present
|
* Fails if the object's key is already present
|
||||||
* in the object store.
|
* in the object store.
|
||||||
*/
|
*/
|
||||||
add(storeName, val): QueryRoot {
|
add(storeName: string, val: any): QueryRoot {
|
||||||
const doAdd = (tx: IDBTransaction) => {
|
const doAdd = (tx: IDBTransaction) => {
|
||||||
tx.objectStore(storeName).add(val);
|
tx.objectStore(storeName).add(val);
|
||||||
};
|
};
|
||||||
@ -306,16 +314,16 @@ class QueryRoot {
|
|||||||
/**
|
/**
|
||||||
* Get one object from a store by its key.
|
* Get one object from a store by its key.
|
||||||
*/
|
*/
|
||||||
get(storeName, key): Promise<any> {
|
get(storeName: any, key: any): Promise<any> {
|
||||||
if (key === void 0) {
|
if (key === void 0) {
|
||||||
throw Error("key must not be undefined");
|
throw Error("key must not be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
const {resolve, promise} = openPromise();
|
const {resolve, promise} = openPromise();
|
||||||
|
|
||||||
const doGet = (tx) => {
|
const doGet = (tx: IDBTransaction) => {
|
||||||
const req = tx.objectStore(storeName).get(key);
|
const req = tx.objectStore(storeName).get(key);
|
||||||
req.onsuccess = (r) => {
|
req.onsuccess = () => {
|
||||||
resolve(req.result);
|
resolve(req.result);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -329,16 +337,16 @@ class QueryRoot {
|
|||||||
/**
|
/**
|
||||||
* Get one object from a store by its key.
|
* Get one object from a store by its key.
|
||||||
*/
|
*/
|
||||||
getIndexed(storeName, indexName, key): Promise<any> {
|
getIndexed(storeName: string, indexName: string, key: any): Promise<any> {
|
||||||
if (key === void 0) {
|
if (key === void 0) {
|
||||||
throw Error("key must not be undefined");
|
throw Error("key must not be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
const {resolve, promise} = openPromise();
|
const {resolve, promise} = openPromise();
|
||||||
|
|
||||||
const doGetIndexed = (tx) => {
|
const doGetIndexed = (tx: IDBTransaction) => {
|
||||||
const req = tx.objectStore(storeName).index(indexName).get(key);
|
const req = tx.objectStore(storeName).index(indexName).get(key);
|
||||||
req.onsuccess = (r) => {
|
req.onsuccess = () => {
|
||||||
resolve(req.result);
|
resolve(req.result);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -356,7 +364,7 @@ class QueryRoot {
|
|||||||
if (this.kickoffPromise) {
|
if (this.kickoffPromise) {
|
||||||
return this.kickoffPromise;
|
return this.kickoffPromise;
|
||||||
}
|
}
|
||||||
this.kickoffPromise = new Promise((resolve, reject) => {
|
this.kickoffPromise = new Promise<void>((resolve, reject) => {
|
||||||
if (this.work.length == 0) {
|
if (this.work.length == 0) {
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
@ -376,8 +384,8 @@ class QueryRoot {
|
|||||||
/**
|
/**
|
||||||
* Delete an object by from the given object store.
|
* Delete an object by from the given object store.
|
||||||
*/
|
*/
|
||||||
delete(storeName: string, key): QueryRoot {
|
delete(storeName: string, key: any): QueryRoot {
|
||||||
const doDelete = (tx) => {
|
const doDelete = (tx: IDBTransaction) => {
|
||||||
tx.objectStore(storeName).delete(key);
|
tx.objectStore(storeName).delete(key);
|
||||||
};
|
};
|
||||||
this.addWork(doDelete, storeName, true);
|
this.addWork(doDelete, storeName, true);
|
||||||
@ -387,17 +395,21 @@ class QueryRoot {
|
|||||||
/**
|
/**
|
||||||
* Low-level function to add a task to the internal work queue.
|
* Low-level function to add a task to the internal work queue.
|
||||||
*/
|
*/
|
||||||
addWork(workFn: (IDBTransaction) => void,
|
addWork(workFn: (t: IDBTransaction) => void,
|
||||||
storeName: string,
|
storeName?: string,
|
||||||
isWrite: boolean) {
|
isWrite?: boolean) {
|
||||||
|
this.work.push(workFn);
|
||||||
|
if (storeName) {
|
||||||
|
this.addStoreAccess(storeName, isWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addStoreAccess(storeName: string, isWrite?: boolean) {
|
||||||
if (storeName) {
|
if (storeName) {
|
||||||
this.stores.add(storeName);
|
this.stores.add(storeName);
|
||||||
}
|
}
|
||||||
if (isWrite) {
|
if (isWrite) {
|
||||||
this.hasWrite = true;
|
this.hasWrite = true;
|
||||||
}
|
}
|
||||||
if (workFn) {
|
|
||||||
this.work.push(workFn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -392,5 +392,5 @@ export interface CheckRepurchaseResult {
|
|||||||
|
|
||||||
|
|
||||||
export interface Notifier {
|
export interface Notifier {
|
||||||
notify();
|
notify(): void;
|
||||||
}
|
}
|
||||||
|
@ -154,12 +154,12 @@ interface Transaction {
|
|||||||
export interface Badge {
|
export interface Badge {
|
||||||
setText(s: string): void;
|
setText(s: string): void;
|
||||||
setColor(c: string): void;
|
setColor(c: string): void;
|
||||||
startBusy();
|
startBusy(): void;
|
||||||
stopBusy();
|
stopBusy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function deepEquals(x, y) {
|
function deepEquals(x: any, y: any): boolean {
|
||||||
if (x === y) {
|
if (x === y) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getTalerStampSec(stamp: string): number {
|
function getTalerStampSec(stamp: string): number|null {
|
||||||
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return null;
|
return null;
|
||||||
@ -188,7 +188,7 @@ function getTalerStampSec(stamp: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setTimeout(f, t) {
|
function setTimeout(f: any, t: number) {
|
||||||
return chrome.extension.getBackgroundPage().setTimeout(f, t);
|
return chrome.extension.getBackgroundPage().setTimeout(f, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,13 +211,13 @@ interface HttpRequestLibrary {
|
|||||||
|
|
||||||
get(url: string|uri.URI): Promise<HttpResponse>;
|
get(url: string|uri.URI): Promise<HttpResponse>;
|
||||||
|
|
||||||
postJson(url: string|uri.URI, body): Promise<HttpResponse>;
|
postJson(url: string|uri.URI, body: any): Promise<HttpResponse>;
|
||||||
|
|
||||||
postForm(url: string|uri.URI, form): Promise<HttpResponse>;
|
postForm(url: string|uri.URI, form: any): Promise<HttpResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function copy(o) {
|
function copy(o: any) {
|
||||||
return JSON.parse(JSON.stringify(o));
|
return JSON.parse(JSON.stringify(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,11 +240,18 @@ interface KeyUpdateInfo {
|
|||||||
function getWithdrawDenomList(amountAvailable: AmountJson,
|
function getWithdrawDenomList(amountAvailable: AmountJson,
|
||||||
denoms: Denomination[]): Denomination[] {
|
denoms: Denomination[]): Denomination[] {
|
||||||
let remaining = Amounts.copy(amountAvailable);
|
let remaining = Amounts.copy(amountAvailable);
|
||||||
let ds: Denomination[] = [];
|
const ds: Denomination[] = [];
|
||||||
|
|
||||||
|
console.log("available denoms");
|
||||||
|
console.log(denoms);
|
||||||
|
|
||||||
denoms = denoms.filter(isWithdrawableDenom);
|
denoms = denoms.filter(isWithdrawableDenom);
|
||||||
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
|
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
|
||||||
|
|
||||||
|
console.log("withdrawable denoms");
|
||||||
|
console.log(denoms);
|
||||||
|
|
||||||
|
|
||||||
// This is an arbitrary number of coins
|
// This is an arbitrary number of coins
|
||||||
// we can withdraw in one go. It's not clear if this limit
|
// we can withdraw in one go. It's not clear if this limit
|
||||||
// is useful ...
|
// is useful ...
|
||||||
@ -355,7 +362,7 @@ export class Wallet {
|
|||||||
|
|
||||||
let x: number;
|
let x: number;
|
||||||
|
|
||||||
function storeExchangeCoin(mc, url) {
|
function storeExchangeCoin(mc: any, url: string) {
|
||||||
let exchange: IExchangeInfo = mc[0];
|
let exchange: IExchangeInfo = mc[0];
|
||||||
console.log("got coin for exchange", url);
|
console.log("got coin for exchange", url);
|
||||||
let coin: Coin = mc[1];
|
let coin: Coin = mc[1];
|
||||||
@ -366,18 +373,16 @@ export class Wallet {
|
|||||||
exchange.baseUrl);
|
exchange.baseUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cd = {
|
let denom = exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub);
|
||||||
coin: coin,
|
if (!denom) {
|
||||||
denom: exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub)
|
|
||||||
};
|
|
||||||
if (!cd.denom) {
|
|
||||||
console.warn("denom not found (database inconsistent)");
|
console.warn("denom not found (database inconsistent)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cd.denom.value.currency !== paymentAmount.currency) {
|
if (denom.value.currency !== paymentAmount.currency) {
|
||||||
console.warn("same pubkey for different currencies");
|
console.warn("same pubkey for different currencies");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let cd = {coin, denom};
|
||||||
let x = m[url];
|
let x = m[url];
|
||||||
if (!x) {
|
if (!x) {
|
||||||
m[url] = [cd];
|
m[url] = [cd];
|
||||||
@ -464,7 +469,7 @@ export class Wallet {
|
|||||||
private recordConfirmPay(offer: Offer,
|
private recordConfirmPay(offer: Offer,
|
||||||
payCoinInfo: PayCoinInfo,
|
payCoinInfo: PayCoinInfo,
|
||||||
chosenExchange: string): Promise<void> {
|
chosenExchange: string): Promise<void> {
|
||||||
let payReq = {};
|
let payReq: any = {};
|
||||||
payReq["amount"] = offer.contract.amount;
|
payReq["amount"] = offer.contract.amount;
|
||||||
payReq["coins"] = payCoinInfo.map((x) => x.sig);
|
payReq["coins"] = payCoinInfo.map((x) => x.sig);
|
||||||
payReq["H_contract"] = offer.H_contract;
|
payReq["H_contract"] = offer.H_contract;
|
||||||
@ -581,7 +586,7 @@ export class Wallet {
|
|||||||
* Retrieve all necessary information for looking up the contract
|
* Retrieve all necessary information for looking up the contract
|
||||||
* with the given hash.
|
* with the given hash.
|
||||||
*/
|
*/
|
||||||
executePayment(H_contract): Promise<any> {
|
executePayment(H_contract: string): Promise<any> {
|
||||||
return Promise.resolve().then(() => {
|
return Promise.resolve().then(() => {
|
||||||
return Query(this.db)
|
return Query(this.db)
|
||||||
.get("transactions", H_contract)
|
.get("transactions", H_contract)
|
||||||
@ -607,7 +612,7 @@ export class Wallet {
|
|||||||
* First fetch information requred to withdraw from the reserve,
|
* First fetch information requred to withdraw from the reserve,
|
||||||
* then deplete the reserve, withdrawing coins until it is empty.
|
* then deplete the reserve, withdrawing coins until it is empty.
|
||||||
*/
|
*/
|
||||||
private processReserve(reserveRecord): void {
|
private processReserve(reserveRecord: any): void {
|
||||||
let retryDelayMs = 100;
|
let retryDelayMs = 100;
|
||||||
const opId = "reserve-" + reserveRecord.reserve_pub;
|
const opId = "reserve-" + reserveRecord.reserve_pub;
|
||||||
this.startOperation(opId);
|
this.startOperation(opId);
|
||||||
@ -637,7 +642,7 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private processPreCoin(preCoin, retryDelayMs = 100): void {
|
private processPreCoin(preCoin: any, retryDelayMs = 100): void {
|
||||||
this.withdrawExecute(preCoin)
|
this.withdrawExecute(preCoin)
|
||||||
.then((c) => this.storeCoin(c))
|
.then((c) => this.storeCoin(c))
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@ -803,7 +808,7 @@ export class Wallet {
|
|||||||
/**
|
/**
|
||||||
* Withdraw coins from a reserve until it is empty.
|
* Withdraw coins from a reserve until it is empty.
|
||||||
*/
|
*/
|
||||||
private depleteReserve(reserve, exchange: IExchangeInfo): Promise<void> {
|
private depleteReserve(reserve: any, exchange: IExchangeInfo): Promise<void> {
|
||||||
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
|
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
|
||||||
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
|
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
|
||||||
denomsAvailable);
|
denomsAvailable);
|
||||||
@ -912,7 +917,7 @@ export class Wallet {
|
|||||||
* Optionally link the reserve entry to the new or existing
|
* Optionally link the reserve entry to the new or existing
|
||||||
* exchange entry in then DB.
|
* exchange entry in then DB.
|
||||||
*/
|
*/
|
||||||
updateExchangeFromUrl(baseUrl): Promise<IExchangeInfo> {
|
updateExchangeFromUrl(baseUrl: string): Promise<IExchangeInfo> {
|
||||||
baseUrl = canonicalizeBaseUrl(baseUrl);
|
baseUrl = canonicalizeBaseUrl(baseUrl);
|
||||||
let reqUrl = URI("keys").absoluteTo(baseUrl);
|
let reqUrl = URI("keys").absoluteTo(baseUrl);
|
||||||
return this.http.get(reqUrl).then((resp) => {
|
return this.http.get(reqUrl).then((resp) => {
|
||||||
@ -927,8 +932,8 @@ export class Wallet {
|
|||||||
|
|
||||||
private updateExchangeFromJson(baseUrl: string,
|
private updateExchangeFromJson(baseUrl: string,
|
||||||
exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
|
exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
|
||||||
let updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
|
const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
|
||||||
if (!updateTimeSec) {
|
if (updateTimeSec === null) {
|
||||||
throw Error("invalid update time");
|
throw Error("invalid update time");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,7 +952,7 @@ export class Wallet {
|
|||||||
console.log("making fresh exchange");
|
console.log("making fresh exchange");
|
||||||
} else {
|
} else {
|
||||||
if (updateTimeSec < r.last_update_time) {
|
if (updateTimeSec < r.last_update_time) {
|
||||||
console.log("outdated /keys, not updating")
|
console.log("outdated /keys, not updating");
|
||||||
return Promise.resolve(r);
|
return Promise.resolve(r);
|
||||||
}
|
}
|
||||||
exchangeInfo = r;
|
exchangeInfo = r;
|
||||||
@ -966,9 +971,9 @@ export class Wallet {
|
|||||||
{indexName: "exchangeBaseUrl", only: baseUrl})
|
{indexName: "exchangeBaseUrl", only: baseUrl})
|
||||||
.reduce((coin: Coin, suspendedCoins: Coin[]) => {
|
.reduce((coin: Coin, suspendedCoins: Coin[]) => {
|
||||||
if (!updatedExchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) {
|
if (!updatedExchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) {
|
||||||
return [].concat(suspendedCoins, [coin]);
|
return Array.prototype.concat(suspendedCoins, [coin]);
|
||||||
}
|
}
|
||||||
return [].concat(suspendedCoins);
|
return Array.prototype.concat(suspendedCoins);
|
||||||
}, [])
|
}, [])
|
||||||
.then((suspendedCoins: Coin[]) => {
|
.then((suspendedCoins: Coin[]) => {
|
||||||
let q = Query(this.db);
|
let q = Query(this.db);
|
||||||
@ -999,8 +1004,8 @@ export class Wallet {
|
|||||||
let found = false;
|
let found = false;
|
||||||
for (let oldDenom of exchangeInfo.all_denoms) {
|
for (let oldDenom of exchangeInfo.all_denoms) {
|
||||||
if (oldDenom.denom_pub === newDenom.denom_pub) {
|
if (oldDenom.denom_pub === newDenom.denom_pub) {
|
||||||
let a = Object.assign({}, oldDenom);
|
let a: any = Object.assign({}, oldDenom);
|
||||||
let b = Object.assign({}, newDenom);
|
let b: any = Object.assign({}, newDenom);
|
||||||
// pub hash is only there for convenience in the wallet
|
// pub hash is only there for convenience in the wallet
|
||||||
delete a["pub_hash"];
|
delete a["pub_hash"];
|
||||||
delete b["pub_hash"];
|
delete b["pub_hash"];
|
||||||
@ -1048,7 +1053,7 @@ export class Wallet {
|
|||||||
* that is currenctly available for spending in the wallet.
|
* that is currenctly available for spending in the wallet.
|
||||||
*/
|
*/
|
||||||
getBalances(): Promise<any> {
|
getBalances(): Promise<any> {
|
||||||
function collectBalances(c: Coin, byCurrency) {
|
function collectBalances(c: Coin, byCurrency: any) {
|
||||||
if (c.suspended) {
|
if (c.suspended) {
|
||||||
return byCurrency;
|
return byCurrency;
|
||||||
}
|
}
|
||||||
@ -1074,7 +1079,7 @@ export class Wallet {
|
|||||||
* Retrive the full event history for this wallet.
|
* Retrive the full event history for this wallet.
|
||||||
*/
|
*/
|
||||||
getHistory(): Promise<any> {
|
getHistory(): Promise<any> {
|
||||||
function collect(x, acc) {
|
function collect(x: any, acc: any) {
|
||||||
acc.push(x);
|
acc.push(x);
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
@ -1099,7 +1104,7 @@ export class Wallet {
|
|||||||
[contract.merchant_pub, contract.repurchase_correlation_id])
|
[contract.merchant_pub, contract.repurchase_correlation_id])
|
||||||
.then((result: Transaction) => {
|
.then((result: Transaction) => {
|
||||||
console.log("db result", result);
|
console.log("db result", result);
|
||||||
let isRepurchase;
|
let isRepurchase: boolean;
|
||||||
if (result) {
|
if (result) {
|
||||||
console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
|
console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
|
||||||
return {
|
return {
|
||||||
|
@ -15,7 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {Wallet, Offer, Badge, ConfirmReserveRequest, CreateReserveRequest} from "./wallet";
|
import {
|
||||||
|
Wallet,
|
||||||
|
Offer,
|
||||||
|
Badge,
|
||||||
|
ConfirmReserveRequest,
|
||||||
|
CreateReserveRequest
|
||||||
|
} from "./wallet";
|
||||||
import {deleteDb, exportDb, openTalerDb} from "./db";
|
import {deleteDb, exportDb, openTalerDb} from "./db";
|
||||||
import {BrowserHttpLib} from "./http";
|
import {BrowserHttpLib} from "./http";
|
||||||
import {Checkable} from "./checkable";
|
import {Checkable} from "./checkable";
|
||||||
@ -48,11 +54,17 @@ function makeHandlers(db: IDBDatabase,
|
|||||||
return exportDb(db);
|
return exportDb(db);
|
||||||
},
|
},
|
||||||
["ping"]: function(detail, sender) {
|
["ping"]: function(detail, sender) {
|
||||||
return Promise.resolve({});
|
if (!sender || !sender.tab || !sender.tab.id) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
let id: number = sender.tab.id;
|
||||||
|
let info: any = <any>paymentRequestCookies[id];
|
||||||
|
delete paymentRequestCookies[id];
|
||||||
|
return Promise.resolve(info);
|
||||||
},
|
},
|
||||||
["reset"]: function(detail, sender) {
|
["reset"]: function(detail, sender) {
|
||||||
if (db) {
|
if (db) {
|
||||||
let tx = db.transaction(db.objectStoreNames, 'readwrite');
|
let tx = db.transaction(Array.from(db.objectStoreNames), 'readwrite');
|
||||||
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
for (let i = 0; i < db.objectStoreNames.length; i++) {
|
||||||
tx.objectStore(db.objectStoreNames[i]).clear();
|
tx.objectStore(db.objectStoreNames[i]).clear();
|
||||||
}
|
}
|
||||||
@ -81,7 +93,7 @@ function makeHandlers(db: IDBDatabase,
|
|||||||
return wallet.confirmReserve(req);
|
return wallet.confirmReserve(req);
|
||||||
},
|
},
|
||||||
["confirm-pay"]: function(detail, sender) {
|
["confirm-pay"]: function(detail, sender) {
|
||||||
let offer;
|
let offer: Offer;
|
||||||
try {
|
try {
|
||||||
offer = Offer.checked(detail.offer);
|
offer = Offer.checked(detail.offer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -100,7 +112,7 @@ function makeHandlers(db: IDBDatabase,
|
|||||||
return wallet.confirmPay(offer);
|
return wallet.confirmPay(offer);
|
||||||
},
|
},
|
||||||
["check-pay"]: function(detail, sender) {
|
["check-pay"]: function(detail, sender) {
|
||||||
let offer;
|
let offer: Offer;
|
||||||
try {
|
try {
|
||||||
offer = Offer.checked(detail.offer);
|
offer = Offer.checked(detail.offer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -173,14 +185,14 @@ class ChromeBadge implements Badge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function dispatch(handlers, req, sender, sendResponse) {
|
function dispatch(handlers: any, req: any, sender: any, sendResponse: any) {
|
||||||
if (req.type in handlers) {
|
if (req.type in handlers) {
|
||||||
Promise
|
Promise
|
||||||
.resolve()
|
.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const p = handlers[req.type](req.detail, sender);
|
const p = handlers[req.type](req.detail, sender);
|
||||||
|
|
||||||
return p.then((r) => {
|
return p.then((r: any) => {
|
||||||
sendResponse(r);
|
sendResponse(r);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -231,12 +243,58 @@ class ChromeNotifier implements Notifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from tab ID to payment information (if any).
|
||||||
|
*/
|
||||||
|
let paymentRequestCookies: {[n: number]: any} = {};
|
||||||
|
|
||||||
|
function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
|
||||||
|
url: string, tabId: number): any {
|
||||||
|
const headers: {[s: string]: string} = {};
|
||||||
|
for (let kv of headerList) {
|
||||||
|
if (kv.value) {
|
||||||
|
headers[kv.name.toLowerCase()] = kv.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const contractUrl = headers["x-taler-contract-url"];
|
||||||
|
if (contractUrl !== undefined) {
|
||||||
|
paymentRequestCookies[tabId] = {type: "fetch", contractUrl};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contractHash = headers["x-taler-contract-hash"];
|
||||||
|
|
||||||
|
if (contractHash !== undefined) {
|
||||||
|
const payUrl = headers["x-taler-pay-url"];
|
||||||
|
if (payUrl === undefined) {
|
||||||
|
console.log("malformed 402, X-Taler-Pay-Url missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offer URL is optional
|
||||||
|
const offerUrl = headers["x-taler-offer-url"];
|
||||||
|
paymentRequestCookies[tabId] = {
|
||||||
|
type: "execute",
|
||||||
|
offerUrl,
|
||||||
|
payUrl,
|
||||||
|
contractHash
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// looks like it's not a taler request, it might be
|
||||||
|
// for a different payment system (or the shop is buggy)
|
||||||
|
console.log("ignoring non-taler 402 response");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function wxMain() {
|
export function wxMain() {
|
||||||
chrome.browserAction.setBadgeText({text: ""});
|
chrome.browserAction.setBadgeText({text: ""});
|
||||||
|
|
||||||
chrome.tabs.query({}, function(tabs) {
|
chrome.tabs.query({}, function(tabs) {
|
||||||
for (let tab of tabs) {
|
for (let tab of tabs) {
|
||||||
if (!tab.url) {
|
if (!tab.url || !tab.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let uri = URI(tab.url);
|
let uri = URI(tab.url);
|
||||||
@ -255,11 +313,14 @@ export function wxMain() {
|
|||||||
console.error("could not open database");
|
console.error("could not open database");
|
||||||
console.error(e);
|
console.error(e);
|
||||||
})
|
})
|
||||||
.then((db) => {
|
.then((db: IDBDatabase) => {
|
||||||
let http = new BrowserHttpLib();
|
let http = new BrowserHttpLib();
|
||||||
let badge = new ChromeBadge();
|
let badge = new ChromeBadge();
|
||||||
let notifier = new ChromeNotifier();
|
let notifier = new ChromeNotifier();
|
||||||
let wallet = new Wallet(db, http, badge, notifier);
|
let wallet = new Wallet(db, http, badge, notifier);
|
||||||
|
|
||||||
|
// Handlers for messages coming directly from the content
|
||||||
|
// script on the page
|
||||||
let handlers = makeHandlers(db, wallet);
|
let handlers = makeHandlers(db, wallet);
|
||||||
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
|
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
|
||||||
try {
|
try {
|
||||||
@ -276,6 +337,19 @@ export function wxMain() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handlers for catching HTTP requests
|
||||||
|
chrome.webRequest.onHeadersReceived.addListener((details) => {
|
||||||
|
if (details.statusCode != 402) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`got 402 from ${details.url}`);
|
||||||
|
return handleHttpPayment(details.responseHeaders || [],
|
||||||
|
details.url,
|
||||||
|
details.tabId);
|
||||||
|
}, {urls: ["<all_urls>"]}, ["responseHeaders", "blocking"]);
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error("could not initialize wallet messaging");
|
console.error("could not initialize wallet messaging");
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
"tabs",
|
"tabs",
|
||||||
|
"webRequest",
|
||||||
|
"webRequestBlocking",
|
||||||
"http://*/*",
|
"http://*/*",
|
||||||
"https://*/*"
|
"https://*/*"
|
||||||
],
|
],
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"po2json": "git+https://github.com/mikeedwards/po2json",
|
"po2json": "git+https://github.com/mikeedwards/po2json",
|
||||||
"systemjs": "^0.19.14",
|
"systemjs": "^0.19.14",
|
||||||
"through2": "^2.0.1",
|
"through2": "^2.0.1",
|
||||||
"typescript": "^1.9.0-dev.20160225",
|
"typescript": "^2.0.2",
|
||||||
"typhonjs-istanbul-instrument-jspm": "^0.1.0",
|
"typhonjs-istanbul-instrument-jspm": "^0.1.0",
|
||||||
"vinyl": "^1.1.1"
|
"vinyl": "^1.1.1"
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
<script src="../lib/vendor/lodash.core.min.js"></script>
|
<script src="../lib/vendor/lodash.core.min.js"></script>
|
||||||
<script src="../lib/vendor/system-csp-production.src.js"></script>
|
<script src="../lib/vendor/system-csp-production.src.js"></script>
|
||||||
<script src="../lib/vendor/jed.js"></script>
|
<script src="../lib/vendor/jed.js"></script>
|
||||||
<script src="../i18n/strings.js"></script>
|
|
||||||
<script src="../lib/i18n.js"></script>
|
<script src="../lib/i18n.js"></script>
|
||||||
|
<script src="../i18n/strings.js"></script>
|
||||||
<script src="../lib/module-trampoline.js"></script>
|
<script src="../lib/module-trampoline.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -26,11 +26,11 @@
|
|||||||
import MithrilComponent = _mithril.MithrilComponent;
|
import MithrilComponent = _mithril.MithrilComponent;
|
||||||
import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
|
import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
|
||||||
import m from "mithril";
|
import m from "mithril";
|
||||||
import {Contract} from "../lib/wallet/types";
|
import {Contract, AmountJson} from "../lib/wallet/types";
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
function prettyAmount(amount) {
|
function prettyAmount(amount: AmountJson) {
|
||||||
let v = amount.value + amount.fraction / 1e6;
|
let v = amount.value + amount.fraction / 1e6;
|
||||||
return `${v.toFixed(2)} ${amount.currency}`;
|
return `${v.toFixed(2)} ${amount.currency}`;
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ const Details = {
|
|||||||
controller() {
|
controller() {
|
||||||
return {collapsed: m.prop(true)};
|
return {collapsed: m.prop(true)};
|
||||||
},
|
},
|
||||||
view(ctrl, contract: Contract) {
|
view(ctrl: any, contract: Contract) {
|
||||||
if (ctrl.collapsed()) {
|
if (ctrl.collapsed()) {
|
||||||
return m("div", [
|
return m("div", [
|
||||||
m("button.linky", {
|
m("button.linky", {
|
||||||
@ -71,11 +71,11 @@ export function main() {
|
|||||||
let offer = JSON.parse(query.offer);
|
let offer = JSON.parse(query.offer);
|
||||||
console.dir(offer);
|
console.dir(offer);
|
||||||
let contract = offer.contract;
|
let contract = offer.contract;
|
||||||
let error = null;
|
let error: string|null = null;
|
||||||
let payDisabled = true;
|
let payDisabled = true;
|
||||||
|
|
||||||
var Contract = {
|
var Contract = {
|
||||||
view(ctrl) {
|
view(ctrl: any) {
|
||||||
return [
|
return [
|
||||||
m("p",
|
m("p",
|
||||||
i18n.parts`${m("strong", contract.merchant.name)}
|
i18n.parts`${m("strong", contract.merchant.name)}
|
||||||
@ -95,7 +95,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
m.mount(document.getElementById("contract"), Contract);
|
m.mount(document.getElementById("contract")!, Contract);
|
||||||
|
|
||||||
function checkPayment() {
|
function checkPayment() {
|
||||||
chrome.runtime.sendMessage({type: 'check-pay', detail: {offer}}, (resp) => {
|
chrome.runtime.sendMessage({type: 'check-pay', detail: {offer}}, (resp) => {
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
<script src="../lib/vendor/mithril.js"></script>
|
<script src="../lib/vendor/mithril.js"></script>
|
||||||
<script src="../lib/vendor/system-csp-production.src.js"></script>
|
<script src="../lib/vendor/system-csp-production.src.js"></script>
|
||||||
<script src="../lib/vendor/jed.js"></script>
|
<script src="../lib/vendor/jed.js"></script>
|
||||||
<script src="../i18n/strings.js"></script>
|
|
||||||
<script src="../lib/i18n.js"></script>
|
<script src="../lib/i18n.js"></script>
|
||||||
|
<script src="../i18n/strings.js"></script>
|
||||||
<script src="../lib/module-trampoline.js"></script>
|
<script src="../lib/module-trampoline.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
import {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers";
|
import {amountToPretty, canonicalizeBaseUrl} from "../lib/wallet/helpers";
|
||||||
import {AmountJson, CreateReserveResponse} from "../lib/wallet/types";
|
import {AmountJson, CreateReserveResponse} from "../lib/wallet/types";
|
||||||
import m from "mithril";
|
import m from "mithril";
|
||||||
import {IExchangeInfo} from "../lib/wallet/types";
|
|
||||||
import {ReserveCreationInfo, Amounts} from "../lib/wallet/types";
|
import {ReserveCreationInfo, Amounts} from "../lib/wallet/types";
|
||||||
import MithrilComponent = _mithril.MithrilComponent;
|
import MithrilComponent = _mithril.MithrilComponent;
|
||||||
import {Denomination} from "../lib/wallet/types";
|
import {Denomination} from "../lib/wallet/types";
|
||||||
@ -41,10 +40,10 @@ import {getReserveCreationInfo} from "../lib/wallet/wxApi";
|
|||||||
*/
|
*/
|
||||||
class DelayTimer {
|
class DelayTimer {
|
||||||
ms: number;
|
ms: number;
|
||||||
f;
|
f: () => void;
|
||||||
timerId: number = null;
|
timerId: number|undefined = undefined;
|
||||||
|
|
||||||
constructor(ms: number, f) {
|
constructor(ms: number, f: () => void) {
|
||||||
this.f = f;
|
this.f = f;
|
||||||
this.ms = ms;
|
this.ms = ms;
|
||||||
}
|
}
|
||||||
@ -58,7 +57,7 @@ class DelayTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
if (this.timerId !== null) {
|
if (this.timerId != undefined) {
|
||||||
window.clearTimeout(this.timerId);
|
window.clearTimeout(this.timerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,11 +66,10 @@ class DelayTimer {
|
|||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
url = m.prop<string>();
|
url = m.prop<string>();
|
||||||
statusString = null;
|
statusString: string | null = null;
|
||||||
isValidExchange = false;
|
isValidExchange = false;
|
||||||
reserveCreationInfo: ReserveCreationInfo = null;
|
reserveCreationInfo?: ReserveCreationInfo;
|
||||||
private timer: DelayTimer;
|
private timer: DelayTimer;
|
||||||
private request: XMLHttpRequest;
|
|
||||||
amount: AmountJson;
|
amount: AmountJson;
|
||||||
callbackUrl: string;
|
callbackUrl: string;
|
||||||
wtTypes: string[];
|
wtTypes: string[];
|
||||||
@ -97,7 +95,7 @@ class Controller {
|
|||||||
private update() {
|
private update() {
|
||||||
this.timer.stop();
|
this.timer.stop();
|
||||||
const doUpdate = () => {
|
const doUpdate = () => {
|
||||||
this.reserveCreationInfo = null;
|
this.reserveCreationInfo = undefined;
|
||||||
if (!this.url()) {
|
if (!this.url()) {
|
||||||
this.statusString = i18n`Error: URL is empty`;
|
this.statusString = i18n`Error: URL is empty`;
|
||||||
m.redraw(true);
|
m.redraw(true);
|
||||||
@ -126,7 +124,7 @@ class Controller {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log("get exchange info rejected");
|
console.log("get exchange info rejected");
|
||||||
if (e.hasOwnProperty("httpStatus")) {
|
if (e.hasOwnProperty("httpStatus")) {
|
||||||
this.statusString = `Error: request failed with status ${this.request.status}`;
|
this.statusString = `Error: request failed with status ${e.httpStatus}`;
|
||||||
} else if (e.hasOwnProperty("errorResponse")) {
|
} else if (e.hasOwnProperty("errorResponse")) {
|
||||||
let resp = e.errorResponse;
|
let resp = e.errorResponse;
|
||||||
this.statusString = `Error: ${resp.error} (${resp.hint})`;
|
this.statusString = `Error: ${resp.error} (${resp.hint})`;
|
||||||
@ -143,11 +141,7 @@ class Controller {
|
|||||||
reset() {
|
reset() {
|
||||||
this.isValidExchange = false;
|
this.isValidExchange = false;
|
||||||
this.statusString = null;
|
this.statusString = null;
|
||||||
this.reserveCreationInfo = null;
|
this.reserveCreationInfo = undefined;
|
||||||
if (this.request) {
|
|
||||||
this.request.abort();
|
|
||||||
this.request = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmReserve(rci: ReserveCreationInfo,
|
confirmReserve(rci: ReserveCreationInfo,
|
||||||
@ -155,7 +149,7 @@ class Controller {
|
|||||||
amount: AmountJson,
|
amount: AmountJson,
|
||||||
callback_url: string) {
|
callback_url: string) {
|
||||||
const d = {exchange, amount};
|
const d = {exchange, amount};
|
||||||
const cb = (rawResp) => {
|
const cb = (rawResp: any) => {
|
||||||
if (!rawResp) {
|
if (!rawResp) {
|
||||||
throw Error("empty response");
|
throw Error("empty response");
|
||||||
}
|
}
|
||||||
@ -195,127 +189,122 @@ class Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function view(ctrl: Controller): any {
|
function view(ctrl: Controller): any {
|
||||||
let controls = [];
|
function* f(): IterableIterator<any> {
|
||||||
let mx = (x, ...args) => controls.push(m(x, ...args));
|
yield m("p",
|
||||||
|
i18n.parts`You are about to withdraw ${m("strong", amountToPretty(
|
||||||
|
ctrl.amount))} from your bank account into your wallet.`);
|
||||||
|
|
||||||
mx("p",
|
if (ctrl.complexViewRequested || !ctrl.suggestedExchangeUrl) {
|
||||||
i18n.parts`You are about to withdraw ${m("strong", amountToPretty(
|
yield viewComplex(ctrl);
|
||||||
ctrl.amount))} from your bank account into your wallet.`);
|
return;
|
||||||
|
}
|
||||||
if (ctrl.complexViewRequested || !ctrl.suggestedExchangeUrl) {
|
yield viewSimple(ctrl);
|
||||||
return controls.concat(viewComplex(ctrl));
|
|
||||||
}
|
}
|
||||||
|
return Array.from(f());
|
||||||
return controls.concat(viewSimple(ctrl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewSimple(ctrl: Controller) {
|
function viewSimple(ctrl: Controller) {
|
||||||
let controls = [];
|
function *f() {
|
||||||
let mx = (x, ...args) => controls.push(m(x, ...args));
|
if (ctrl.statusString) {
|
||||||
|
yield m("p", "Error: ", ctrl.statusString);
|
||||||
if (ctrl.statusString) {
|
yield m("button.linky", {
|
||||||
mx("p", "Error: ", ctrl.statusString);
|
onclick: () => {
|
||||||
mx("button.linky", {
|
ctrl.complexViewRequested = true;
|
||||||
onclick: () => {
|
}
|
||||||
ctrl.complexViewRequested = true;
|
}, "advanced options");
|
||||||
}
|
}
|
||||||
}, "advanced options");
|
else if (ctrl.reserveCreationInfo != undefined) {
|
||||||
|
yield m("button.accept", {
|
||||||
|
onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo!,
|
||||||
|
ctrl.url(),
|
||||||
|
ctrl.amount,
|
||||||
|
ctrl.callbackUrl),
|
||||||
|
disabled: !ctrl.isValidExchange
|
||||||
|
},
|
||||||
|
"Accept fees and withdraw");
|
||||||
|
yield m("span.spacer");
|
||||||
|
yield m("button.linky", {
|
||||||
|
onclick: () => {
|
||||||
|
ctrl.complexViewRequested = true;
|
||||||
|
}
|
||||||
|
}, "advanced options");
|
||||||
|
let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
|
||||||
|
ctrl.reserveCreationInfo.withdrawFee).amount;
|
||||||
|
yield m("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
|
||||||
|
} else {
|
||||||
|
yield m("p", "Please wait ...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (ctrl.reserveCreationInfo) {
|
|
||||||
mx("button.accept", {
|
return Array.from(f());
|
||||||
onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo,
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function viewComplex(ctrl: Controller) {
|
||||||
|
function *f() {
|
||||||
|
yield m("button.accept", {
|
||||||
|
onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo!,
|
||||||
ctrl.url(),
|
ctrl.url(),
|
||||||
ctrl.amount,
|
ctrl.amount,
|
||||||
ctrl.callbackUrl),
|
ctrl.callbackUrl),
|
||||||
disabled: !ctrl.isValidExchange
|
disabled: !ctrl.isValidExchange
|
||||||
},
|
},
|
||||||
"Accept fees and withdraw");
|
"Accept fees and withdraw");
|
||||||
mx("span.spacer");
|
yield m("span.spacer");
|
||||||
mx("button.linky", {
|
yield m("button.linky", {
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
ctrl.complexViewRequested = true;
|
ctrl.complexViewRequested = false;
|
||||||
}
|
}
|
||||||
}, "advanced options");
|
}, "back to simple view");
|
||||||
let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
|
|
||||||
ctrl.reserveCreationInfo.withdrawFee).amount;
|
yield m("br");
|
||||||
mx("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
|
|
||||||
} else {
|
|
||||||
mx("p", "Please wait ...");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return controls;
|
yield m("input", {
|
||||||
}
|
className: "url",
|
||||||
|
type: "text",
|
||||||
|
spellcheck: false,
|
||||||
|
value: ctrl.url(),
|
||||||
|
oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
|
||||||
|
});
|
||||||
|
|
||||||
|
yield m("br");
|
||||||
|
|
||||||
function viewComplex(ctrl: Controller) {
|
if (ctrl.statusString) {
|
||||||
let controls = [];
|
yield m("p", ctrl.statusString);
|
||||||
let mx = (x, ...args) => controls.push(m(x, ...args));
|
} else if (!ctrl.reserveCreationInfo) {
|
||||||
|
yield m("p", "Checking URL, please wait ...");
|
||||||
mx("button.accept", {
|
|
||||||
onclick: () => ctrl.confirmReserve(ctrl.reserveCreationInfo,
|
|
||||||
ctrl.url(),
|
|
||||||
ctrl.amount,
|
|
||||||
ctrl.callbackUrl),
|
|
||||||
disabled: !ctrl.isValidExchange
|
|
||||||
},
|
|
||||||
"Accept fees and withdraw");
|
|
||||||
mx("span.spacer");
|
|
||||||
mx("button.linky", {
|
|
||||||
onclick: () => {
|
|
||||||
ctrl.complexViewRequested = false;
|
|
||||||
}
|
}
|
||||||
}, "back to simple view");
|
|
||||||
|
|
||||||
mx("br");
|
if (ctrl.reserveCreationInfo) {
|
||||||
|
let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
|
||||||
|
ctrl.reserveCreationInfo.withdrawFee).amount;
|
||||||
mx("input",
|
yield m("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
|
||||||
{
|
if (ctrl.detailCollapsed()) {
|
||||||
className: "url",
|
yield m("button.linky", {
|
||||||
type: "text",
|
onclick: () => {
|
||||||
spellcheck: false,
|
ctrl.detailCollapsed(false);
|
||||||
value: ctrl.url(),
|
}
|
||||||
oninput: m.withAttr("value", ctrl.onUrlChanged.bind(ctrl)),
|
}, "show more details");
|
||||||
});
|
} else {
|
||||||
|
yield m("button.linky", {
|
||||||
mx("br");
|
onclick: () => {
|
||||||
|
ctrl.detailCollapsed(true);
|
||||||
if (ctrl.statusString) {
|
}
|
||||||
mx("p", ctrl.statusString);
|
}, "hide details");
|
||||||
} else if (!ctrl.reserveCreationInfo) {
|
yield m("div", {}, renderReserveCreationDetails(ctrl.reserveCreationInfo))
|
||||||
mx("p", "Checking URL, please wait ...");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ctrl.reserveCreationInfo) {
|
|
||||||
let totalCost = Amounts.add(ctrl.reserveCreationInfo.overhead,
|
|
||||||
ctrl.reserveCreationInfo.withdrawFee).amount;
|
|
||||||
mx("p", `Withdraw cost: ${amountToPretty(totalCost)}`);
|
|
||||||
if (ctrl.detailCollapsed()) {
|
|
||||||
mx("button.linky", {
|
|
||||||
onclick: () => {
|
|
||||||
ctrl.detailCollapsed(false);
|
|
||||||
}
|
|
||||||
}, "show more details");
|
|
||||||
} else {
|
|
||||||
mx("button.linky", {
|
|
||||||
onclick: () => {
|
|
||||||
ctrl.detailCollapsed(true);
|
|
||||||
}
|
|
||||||
}, "hide details");
|
|
||||||
mx("div", {}, renderReserveCreationDetails(ctrl.reserveCreationInfo))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Array.from(f());
|
||||||
return m("div", controls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function renderReserveCreationDetails(rci: ReserveCreationInfo) {
|
function renderReserveCreationDetails(rci: ReserveCreationInfo) {
|
||||||
let denoms = rci.selectedDenoms;
|
let denoms = rci.selectedDenoms;
|
||||||
|
|
||||||
let countByPub = {};
|
let countByPub: {[s: string]: number} = {};
|
||||||
let uniq = [];
|
let uniq: Denomination[] = [];
|
||||||
|
|
||||||
denoms.forEach((x: Denomination) => {
|
denoms.forEach((x: Denomination) => {
|
||||||
let c = countByPub[x.denom_pub] || 0;
|
let c = countByPub[x.denom_pub] || 0;
|
||||||
@ -358,7 +347,7 @@ function renderReserveCreationDetails(rci: ReserveCreationInfo) {
|
|||||||
function getSuggestedExchange(currency: string): Promise<string> {
|
function getSuggestedExchange(currency: string): Promise<string> {
|
||||||
// TODO: make this request go to the wallet backend
|
// TODO: make this request go to the wallet backend
|
||||||
// Right now, this is a stub.
|
// Right now, this is a stub.
|
||||||
const defaultExchange = {
|
const defaultExchange: {[s: string]: string} = {
|
||||||
"KUDOS": "https://exchange.demo.taler.net",
|
"KUDOS": "https://exchange.demo.taler.net",
|
||||||
"PUDOS": "https://exchange.test.taler.net",
|
"PUDOS": "https://exchange.test.taler.net",
|
||||||
};
|
};
|
||||||
@ -373,6 +362,7 @@ function getSuggestedExchange(currency: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
const url = URI(document.location.href);
|
const url = URI(document.location.href);
|
||||||
const query: any = URI.parseQuery(url.query());
|
const query: any = URI.parseQuery(url.query());
|
||||||
@ -383,14 +373,14 @@ export function main() {
|
|||||||
|
|
||||||
getSuggestedExchange(amount.currency)
|
getSuggestedExchange(amount.currency)
|
||||||
.then((suggestedExchangeUrl) => {
|
.then((suggestedExchangeUrl) => {
|
||||||
const controller = () => new Controller(suggestedExchangeUrl, amount, callback_url, wt_types);
|
const controller = function () { return new Controller(suggestedExchangeUrl, amount, callback_url, wt_types); };
|
||||||
var ExchangeSelection = {controller, view};
|
const ExchangeSelection = {controller, view};
|
||||||
m.mount(document.getElementById("exchange-selection"), ExchangeSelection);
|
m.mount(document.getElementById("exchange-selection")!, ExchangeSelection);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
// TODO: provide more context information, maybe factor it out into a
|
// TODO: provide more context information, maybe factor it out into a
|
||||||
// TODO:generic error reporting function or component.
|
// TODO:generic error reporting function or component.
|
||||||
document.body.innerText = `Fatal error: "${e.message}".`;
|
document.body.innerText = `Fatal error: "${e.message}".`;
|
||||||
console.error(`got backend error "${e.message}"`);
|
console.error(`got error "${e.message}"`, e);
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -21,30 +21,37 @@
|
|||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function replacer(match, pIndent, pKey, pVal, pEnd) {
|
function replacer(match: string, pIndent: string, pKey: string, pVal: string,
|
||||||
|
pEnd: string) {
|
||||||
var key = '<span class=json-key>';
|
var key = '<span class=json-key>';
|
||||||
var val = '<span class=json-value>';
|
var val = '<span class=json-value>';
|
||||||
var str = '<span class=json-string>';
|
var str = '<span class=json-string>';
|
||||||
var r = pIndent || '';
|
var r = pIndent || '';
|
||||||
if (pKey)
|
if (pKey) {
|
||||||
r = r + key + pKey.replace(/[": ]/g, '') + '</span>: ';
|
r = r + key + pKey.replace(/[": ]/g, '') + '</span>: ';
|
||||||
if (pVal)
|
}
|
||||||
r = r + (pVal[0] == '"' ? str : val) + pVal + '</span>';
|
if (pVal) {
|
||||||
|
r = r + (pVal[0] == '"' ? str : val) + pVal + '</span>';
|
||||||
|
}
|
||||||
return r + (pEnd || '');
|
return r + (pEnd || '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function prettyPrint(obj) {
|
function prettyPrint(obj: any) {
|
||||||
var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
|
var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
|
||||||
return JSON.stringify(obj, null, 3)
|
return JSON.stringify(obj, null as any, 3)
|
||||||
.replace(/&/g, '&').replace(/\\"/g, '"')
|
.replace(/&/g, '&').replace(/\\"/g, '"')
|
||||||
.replace(/</g, '<').replace(/>/g, '>')
|
.replace(/</g, '<').replace(/>/g, '>')
|
||||||
.replace(jsonLine, replacer);
|
.replace(jsonLine, replacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (e) => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
chrome.runtime.sendMessage({type:'dump-db'}, (resp) => {
|
chrome.runtime.sendMessage({type: 'dump-db'}, (resp) => {
|
||||||
document.getElementById('dump').innerHTML = prettyPrint(resp);
|
const el = document.getElementById('dump');
|
||||||
|
if (!el) {
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
el.innerHTML = prettyPrint(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -29,12 +29,15 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
|
import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
|
||||||
|
import BrowserClickedEvent = chrome.browserAction.BrowserClickedEvent;
|
||||||
|
import {Wallet} from "../lib/wallet/wallet";
|
||||||
|
import {AmountJson} from "../lib/wallet/types";
|
||||||
|
|
||||||
declare var m: any;
|
declare var m: any;
|
||||||
declare var i18n: any;
|
declare var i18n: any;
|
||||||
|
|
||||||
|
|
||||||
function onUpdateNotification(f) {
|
function onUpdateNotification(f: () => void) {
|
||||||
let port = chrome.runtime.connect({name: "notifications"});
|
let port = chrome.runtime.connect({name: "notifications"});
|
||||||
port.onMessage.addListener((msg, port) => {
|
port.onMessage.addListener((msg, port) => {
|
||||||
f();
|
f();
|
||||||
@ -56,7 +59,7 @@ export function main() {
|
|||||||
console.log("this is popup");
|
console.log("this is popup");
|
||||||
|
|
||||||
|
|
||||||
function makeTab(target, name) {
|
function makeTab(target: string, name: string) {
|
||||||
let cssClass = "";
|
let cssClass = "";
|
||||||
if (target == m.route()) {
|
if (target == m.route()) {
|
||||||
cssClass = "active";
|
cssClass = "active";
|
||||||
@ -79,8 +82,8 @@ namespace WalletNavBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function openInExtension(element, isInitialized) {
|
function openInExtension(element: HTMLAnchorElement, isInitialized: boolean) {
|
||||||
element.addEventListener("click", (e) => {
|
element.addEventListener("click", (e: Event) => {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
"url": element.href
|
"url": element.href
|
||||||
});
|
});
|
||||||
@ -88,13 +91,15 @@ function openInExtension(element, isInitialized) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace WalletBalance {
|
namespace WalletBalance {
|
||||||
export function controller() {
|
export function controller() {
|
||||||
return new Controller();
|
return new Controller();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
myWallet;
|
myWallet: any;
|
||||||
gotError = false;
|
gotError = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -128,7 +133,7 @@ namespace WalletBalance {
|
|||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
throw Error("Could not retrieve wallet");
|
throw Error("Could not retrieve wallet");
|
||||||
}
|
}
|
||||||
let listing = _.map(wallet, x => m("p", formatAmount(x)));
|
let listing = _.map(wallet, (x: any) => m("p", formatAmount(x)));
|
||||||
if (listing.length > 0) {
|
if (listing.length > 0) {
|
||||||
return listing;
|
return listing;
|
||||||
}
|
}
|
||||||
@ -141,13 +146,13 @@ namespace WalletBalance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatTimestamp(t) {
|
function formatTimestamp(t: number) {
|
||||||
let x = new Date(t);
|
let x = new Date(t);
|
||||||
return x.toLocaleString();
|
return x.toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatAmount(amount) {
|
function formatAmount(amount: AmountJson) {
|
||||||
let v = amount.value + amount.fraction / 1e6;
|
let v = amount.value + amount.fraction / 1e6;
|
||||||
return `${v.toFixed(2)} ${amount.currency}`;
|
return `${v.toFixed(2)} ${amount.currency}`;
|
||||||
}
|
}
|
||||||
@ -158,7 +163,7 @@ function abbrevKey(s: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function retryPayment(url, contractHash) {
|
function retryPayment(url: string, contractHash: string) {
|
||||||
return function() {
|
return function() {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
"url": substituteFulfillmentUrl(url,
|
"url": substituteFulfillmentUrl(url,
|
||||||
@ -168,7 +173,7 @@ function retryPayment(url, contractHash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatHistoryItem(historyItem) {
|
function formatHistoryItem(historyItem: any) {
|
||||||
const d = historyItem.detail;
|
const d = historyItem.detail;
|
||||||
const t = historyItem.timestamp;
|
const t = historyItem.timestamp;
|
||||||
console.log("hist item", historyItem);
|
console.log("hist item", historyItem);
|
||||||
@ -210,7 +215,7 @@ namespace WalletHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
myHistory;
|
myHistory: any;
|
||||||
gotError = false;
|
gotError = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -287,7 +292,7 @@ var WalletDebug = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function openExtensionPage(page) {
|
function openExtensionPage(page: string) {
|
||||||
return function() {
|
return function() {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
"url": chrome.extension.getURL(page)
|
"url": chrome.extension.getURL(page)
|
||||||
@ -296,7 +301,7 @@ function openExtensionPage(page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function openTab(page) {
|
function openTab(page: string) {
|
||||||
return function() {
|
return function() {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
"url": page
|
"url": page
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as Emsc from '../../lib/wallet/emscriptif';
|
import * as Emsc from '../../lib/wallet/emscriptif';
|
||||||
|
|
||||||
|
|
||||||
declare var HttpMockLib;
|
declare var HttpMockLib: any;
|
||||||
|
|
||||||
export function declareTests(assert, context, it) {
|
export function declareTests(assert: any, context: any, it: any) {
|
||||||
|
|
||||||
it("calls native emscripten code", function() {
|
it("calls native emscripten code", function() {
|
||||||
let x = new Emsc.Amount({value: 42, fraction: 42, currency: "EUR"});
|
let x = new Emsc.Amount({value: 42, fraction: 42, currency: "EUR"});
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"module": "system",
|
"module": "system",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"noLib": true,
|
"noLib": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitAny": true
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib/i18n.ts",
|
"lib/i18n.ts",
|
||||||
"lib/refs.ts",
|
"lib/refs.ts",
|
||||||
|
"lib/shopApi.ts",
|
||||||
"lib/wallet/checkable.ts",
|
"lib/wallet/checkable.ts",
|
||||||
"lib/wallet/cryptoApi.ts",
|
"lib/wallet/cryptoApi.ts",
|
||||||
"lib/wallet/cryptoLib.ts",
|
"lib/wallet/cryptoLib.ts",
|
||||||
|
Loading…
Reference in New Issue
Block a user