refactor / remove lodash dependency

This commit is contained in:
Florian Dold 2016-10-10 00:37:08 +02:00
parent 153765d76e
commit db0fc77698
12 changed files with 259 additions and 19898 deletions

2
README
View File

@ -16,9 +16,7 @@ Dependencies
This project has the following third-party runtime dependencies: This project has the following third-party runtime dependencies:
* URI.js: URL parsing and construction * URI.js: URL parsing and construction
* mithril.js: view-layer based on virtual DOMs
* jed: gettext parsing for JavaScript * jed: gettext parsing for JavaScript
* lodash: standard helper library for JavaScript
* system.js: Module loader for browsers that don't support * system.js: Module loader for browsers that don't support
ES6 modules ES6 modules

16525
lib/decl/lodash.d.ts vendored

File diff suppressed because it is too large Load Diff

926
lib/decl/mithril.d.ts vendored
View File

@ -1,926 +0,0 @@
// Mithril type definitions for Typescript
/**
* This is the module containing all the types/declarations/etc. for Mithril
*/
declare module _mithril {
interface MithrilStatic {
/**
* Creates a virtual element for use with m.render, m.mount, etc.
*
* @param selector A simple CSS selector. May include SVG tags. Nested
* selectors are not supported.
* @param attributes Attributes to add. Any DOM attribute may be used
* as an attribute, although innerHTML and the like may be overwritten
* silently.
* @param children Child elements, components, and text to add.
* @return A virtual element.
*
* @see m.render
* @see m.mount
* @see m.component
*/
<T extends MithrilController>(
selector: string,
attributes: MithrilAttributes,
...children: Array<string |
MithrilVirtualElement<T> |
MithrilComponent<T>>
): MithrilVirtualElement<T>;
/**
* Initializes a component for use with m.render, m.mount, etc.
*
* @param component A component.
* @param args Arguments to optionally pass to the component.
* @return A component.
*
* @see m.render
* @see m.mount
* @see m
*/
<T extends MithrilController>(
component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/**
* Creates a virtual element for use with m.render, m.mount, etc.
*
* @param selector A simple CSS selector. Nested selectors are not
* supported.
* @param children Child elements, components, and text to add.
* @return A virtual element.
*
* @see m.render
* @see m.mount
* @see m.component
*/
<T extends MithrilController>(
selector: string,
...children: Array<string |
MithrilVirtualElement<T> |
MithrilComponent<T>>
): MithrilVirtualElement<T>;
/**
* Initializes a component for use with m.render, m.mount, etc.
* Shorthand for m.component.
*
* @param selector A component.
* @param args Arguments to optionally pass to the component.
* @return A component.
*
* @see m.render
* @see m.mount
* @see m.component
*/
<T extends MithrilController>(
component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/**
* Creates a getter-setter function that wraps a Mithril promise. Useful
* for uniform data access, m.withAttr, etc.
*
* @param promise A thennable to initialize the property with. It may
* optionally be a Mithril promise.
* @return A getter-setter function wrapping the promise.
*
* @see m.withAttr
*/
prop<T>(promise: Thennable<T>) : MithrilPromiseProperty<T>;
/**
* Creates a getter-setter function that wraps a simple value. Useful
* for uniform data access, m.withAttr, etc.
*
* @param value A value to initialize the property with
* @return A getter-setter function wrapping the value.
*
* @see m.withAttr
*/
prop<T>(value: T): MithrilBasicProperty<T>;
/**
* Creates a getter-setter function that wraps a simple value. Useful
* for uniform data access, m.withAttr, etc.
*
* @return A getter-setter function wrapping the value.
*
* @see m.withAttr
*/
prop<T>(): MithrilBasicProperty<T>;
/**
* Returns a event handler that can be bound to an element, firing with
* the specified property.
*
* @param property The property to get from the event.
* @param callback The handler to use the value from the event.
* @return A function suitable for listening to an event.
*/
withAttr(
property: string,
callback: (value: any) => void,
callbackThis: any
): (e: Event) => any;
/**
* Returns a event handler that can be bound to an element, firing with
* the specified property.
*
* @param attributeName Name of the element's attribute to bind to.
* @param property The property to bind.
* @return A function suitable for listening to an event.
*/
withAttr<T>(
attributeName: string,
property: MithrilBasicProperty<T>
) : (e: Event) => any;
/**
* @deprecated Use m.mount instead
*/
module<T extends MithrilController>(
rootElement: Node,
component: MithrilComponent<T>
): T;
/**
* Mounts a component to a base DOM node.
*
* @param rootElement The base node.
* @param component The component to mount.
* @return An instance of the top-level component's controller
*/
mount<T extends MithrilController>(
rootElement: Node,
component: MithrilComponent<T>
): T;
/**
* Initializes a component for use with m.render, m.mount, etc.
*
* @param selector A component.
* @param args Arguments to optionally pass to the component.
* @return A component.
*
* @see m.render
* @see m.mount
* @see m
*/
component<T extends MithrilController>(
component: MithrilComponent<T>,
...args: any[]
): MithrilComponent<T>;
/**
* Trust this string of HTML.
*
* @param html The HTML to trust
* @return A String object instance with an added internal flag to mark
* it as trusted.
*/
trust(html: string): MithrilTrustedString;
/**
* Render a virtual DOM tree.
*
* @param rootElement The base element/node to render the tree from.
* @param children One or more child nodes to add to the tree.
* @param forceRecreation If true, overwrite the entire tree without
* diffing against it.
*/
render<T extends MithrilController>(
rootElement: Element,
children: MithrilVirtualElement<T>|MithrilVirtualElement<T>[],
forceRecreation?: boolean
): void;
redraw: {
/**
* Force a redraw the active component. It redraws asynchronously by
* default to allow for simultaneous events to run before redrawing,
* such as the event combination keypress + input frequently used for
* input.
*
* @param force If true, redraw synchronously.
*/
(force?: boolean): void;
strategy: {
/**
* Gets the current redraw strategy, which returns one of the
* following:
*
* "all" - recreates the DOM tree from scratch
* "diff" - recreates the DOM tree from scratch
* "none" - leaves the DOM tree intact
*
* This is useful for event handlers, which may want to cancel
* the next redraw if the event doesn't update the UI.
*
* @return The current strategy
*/
(): string;
/**
* Sets the current redraw strategy. The parameter must be one of
* the following values:
*
* "all" - recreates the DOM tree from scratch
* "diff" - recreates the DOM tree from scratch
* "none" - leaves the DOM tree intact
*
* This is useful for event handlers, which may want to cancel
* the next redraw if the event doesn't update the UI.
*
* @param value The value to set
* @return The new strategy
*/
(value: string): string;
/**
* @private
* Implementation detail - it's a MithrilBasicProperty instance
*/
toJSON(): string;
}
}
route: {
/**
* Enable routing, mounting a controller based on the route. It
* automatically mounts the components for you, starting with the one
* specified by the default route.
*
* @param rootElement The element to mount the active controller to.
* @param defaultRoute The route to start with.
* @param routes A key-value mapping of pathname to controller.
*/
<T extends MithrilController>(
rootElement: Element,
defaultRoute: string,
routes: MithrilRoutes
): void;
/**
* This allows m.route to be used as the `config` attribute for a
* virtual element, particularly useful for cases like this:
*
* ```ts
* // Note that the '#' is not required in `href`, thanks to the
* `config` setting.
* m("a[href='/dashboard/alicesmith']", {config: m.route});
* ```
*/
<T extends MithrilController>(
element: Element,
isInitialized: boolean,
context?: MithrilContext,
vdom?: MithrilVirtualElement<T>
): void;
/**
* Programmatically redirect to another route.
*
* @param path The route to go to.
* @param params Parameters to pass as a query string.
* @param shouldReplaceHistory Whether to replace the current history
* instead of adding a new one.
*/
(path: string, params?: any, shouldReplaceHistory?: boolean): void;
/**
* Gets the current route.
*
* @return The current route.
*/
(): string;
/**
* Gets a route parameter.
*
* @param key The key to get.
* @return The value associated with the parameter key.
*/
param(key: string): string;
/**
* The current routing mode. This may be changed before calling
* m.route to change the part of the URL used to perform the routing.
*
* The value can be set to one of the following, defaulting to
* "hash":
*
* "search" - Uses the query string. This allows for named anchors to
* work on the page, but changes cause IE8 and lower to refresh the
* page.
*
* "hash" - Uses the hash. This is the only routing mode that does
* not cause page refreshes on any browser, but it does not support
* named anchors.
*
* "pathname" - Uses the URL pathname. This requires server-side
* setup to support bookmarking and page refreshes. It always causes
* page refreshes on IE8 and lower. Note that this requires that the
* application to be run from the root of the URL.
*/
mode: string;
/**
* Serialize an object into a query string.
*
* @param data The data to serialize.
* @return The serialized string.
*/
buildQueryString(data: Object): String
/**
* Parse a query string into an object.
*
* @param data The data to parse.
* @return The parsed object data.
*/
parseQueryString(data: String): Object
}
/**
* Send a request to a server to server. Note that the `url` option is
* required.
*
* @param options The options to use
* @return A promise to the returned data for "GET" requests, or a void
* promise for any other request type.
*
* @see MithrilXHROptions for the available options.
*/
request<T>(options: MithrilXHROptions<T>): MithrilPromise<T>;
deferred: {
/**
* Create a Mithril deferred object. It behaves synchronously if
* possible, an intentional deviation from Promises/A+. Note that
* deferreds are completely separate from the redrawing system, and
* never trigger a redraw on their own.
*
* @return A new Mithril deferred instance.
*
* @see m.deferred.onerror for the error callback called for Error
* subclasses
*/
<T>(): MithrilDeferred<T>;
/**
* A callback for all uncaught native Error subclasses in deferreds.
* This defaults to synchronously rethrowing all errors, a deviation
* from Promises/A+, but the behavior is configurable. To restore
* Promises/A+-compatible behavior. simply set this to a no-op.
*/
onerror(e: Error): void;
}
/**
* Takes a list of promises or thennables and returns a Mithril promise
* that resolves once all in the list are resolved, or rejects if any of
* them reject.
*
* @param promises A list of promises to try to resolve.
* @return A promise that resolves to all the promises if all resolve, or
* rejects with the error contained in the first rejection.
*/
sync<T>(promises: Thennable<T>[]): MithrilPromise<T[]>;
/**
* Use this and endComputation if your views aren't redrawing after
* calls to third-party libraries. For integrating asynchronous code,
* this should be called before any asynchronous work is done. For
* synchronous code, this should be called at the beginning of the
* problematic segment. Note that these calls must be balanced, much like
* braces and parentheses. This is mostly used internally. Prefer
* m.redraw where possible, especially when making repeated calls.
*
* @see endComputation
* @see m.render
*/
startComputation(): void;
/**
* Use startComputation and this if your views aren't redrawing after
* calls to third-party libraries. For integrating asynchronous code,
* this should be called after all asynchronous work completes. For
* synchronous code, this should be called at the end of the problematic
* segment. Note that these calls must be balanced, much like braces and
* parentheses. This is mostly used internally. Prefer m.redraw where
* possible, especially when making repeated calls.
*
* @see startComputation
* @see m.render
*/
endComputation(): void;
/**
* This overwrites the internal version of window used by Mithril.
* It's mostly useful for testing, and is also used internally by
* Mithril to test itself. By default Mithril uses `window` for the
* dependency.
*
* @param mockWindow The mock to use for the window.
* @return The mock that was passed in.
*/
deps(mockWindow: Window): Window;
}
interface MithrilTrustedString extends String {
/** @private Implementation detail. Don't depend on it. */
$trusted: boolean;
}
/**
* The interface for a virtual element. It's best to consider this immutable
* for most use cases.
*
* @see m
*/
interface MithrilVirtualElement<T extends MithrilController> {
/**
* A key to optionally associate with this element.
*/
key?: number;
/**
* The tag name of this element.
*/
tag?: string;
/**
* The attributes of this element.
*/
attrs?: MithrilAttributes;
/**
* The children of this element.
*/
children?: Array<string|MithrilVirtualElement<T>|MithrilComponent<T>>;
}
/**
* An event passed by Mithril to unload event handlers.
*/
interface MithrilEvent {
/**
* Prevent the default behavior of scrolling the page and updating the
* URL on next route change.
*/
preventDefault(): void;
}
/**
* A context object for configuration functions.
*
* @see MithrilElementConfig
*/
interface MithrilContext {
/**
* A function to call when the node is unloaded. Useful for cleanup.
*/
onunload?(): any;
/**
* Set true if the backing DOM node needs to be retained between route
* changes if possible. Set false if this node needs to be recreated
* every single time, regardless of how "different" it is.
*/
retain?: boolean;
}
/**
* This represents a callback function for a virtual element's config
* attribute. It's a low-level function useful for extra cleanup after
* removal from the tree, storing instances of third-party classes that
* need to be associated with the DOM, etc.
*
* @see MithrilAttributes
* @see MithrilContext
*/
interface MithrilElementConfig {
/**
* A callback function for a virtual element's config attribute.
*
* @param element The associated DOM element.
* @param isInitialized Whether this is the first call for the virtual
* element or not.
* @param context The associated context for this element.
* @param vdom The associated virtual element.
*/
<T extends MithrilController>(
element: Element,
isInitialized: boolean,
context: MithrilContext,
vdom: MithrilVirtualElement<T>
): void;
}
/**
* This represents the attributes available for configuring virtual elements,
* beyond the applicable DOM attributes.
*
* @see m
*/
interface MithrilAttributes {
/**
* The class name(s) for this virtual element, as a space-separated list.
*/
className?: string;
/**
* The class name(s) for this virtual element, as a space-separated list.
*/
class?: string;
/**
* A custom, low-level configuration in case this element needs special
* cleanup after removal from the tree.
*
* @see MithrilElementConfig
*/
config?: MithrilElementConfig;
/**
* Any other virtual element properties including attributes and
* event handlers
*/
[property: string]: any;
}
/**
* The basis of a Mithril controller instance.
*/
interface MithrilController {
/**
* An optional handler to call when the associated virtual element is
* destroyed.
*
* @param evt An associated event.
*/
onunload?(evt: MithrilEvent): any;
}
/**
* This represents a controller function.
*
* @see MithrilControllerConstructor
*/
interface MithrilControllerFunction<T extends MithrilController> {
(opts?: any): T;
}
/**
* This represents a controller constructor.
*
* @see MithrilControllerFunction
*/
interface MithrilControllerConstructor<T extends MithrilController> {
new(): T;
}
/**
* This represents a view factory.
*/
interface MithrilView<T extends MithrilController> {
/**
* Creates a view out of virtual elements.
*/
(ctrl: T): MithrilVirtualElement<T>;
}
/**
* This represents a Mithril component.
*
* @see m
* @see m.component
*/
interface MithrilComponent<T extends MithrilController> {
/**
* The component's controller.
*
* @see m.component
*/
controller?: MithrilControllerFunction<T> |
MithrilControllerConstructor<T>;
/**
* Creates a view out of virtual elements.
*
* @see m.component
*/
view(ctrl?: T, opts?: any): MithrilVirtualElement<T>;
}
/**
* This is the base interface for property getter-setters
*
* @see m.prop
*/
interface MithrilProperty<T> {
/**
* Gets the contained value.
*
* @return The contained value.
*/
(): T;
/**
* Sets the contained value.
*
* @param value The new value to set.
* @return The newly set value.
*/
(value: T): T;
}
/**
* This represents a non-promise getter-setter functions.
*
* @see m.prop which returns objects that implement this interface.
*/
interface MithrilBasicProperty<T> extends MithrilProperty<T> {
/**
* Makes this serializable to JSON.
*/
toJSON(): T;
}
/**
* This represents a promise getter-setter function.
*
* @see m.prop which returns objects that implement this interface.
*/
interface MithrilPromiseProperty<T> extends MithrilPromise<T>,
MithrilProperty<MithrilPromise<T>> {
/**
* Gets the contained promise.
*
* @return The contained value.
*/
(): MithrilPromise<T>;
/**
* Sets the contained promise.
*
* @param value The new value to set.
* @return The newly set value.
*/
(value: MithrilPromise<T>): MithrilPromise<T>;
/**
* Sets the contained wrapped value.
*
* @param value The new value to set.
* @return The newly set value.
*/
(value: T): MithrilPromise<T>;
}
/**
* This represents a key-value mapping linking routes to components.
*/
interface MithrilRoutes {
/**
* The key represents the route. The value represents the corresponding
* component.
*/
[key: string]: MithrilComponent<MithrilController>;
}
/**
* This represents a Mithril deferred object.
*/
interface MithrilDeferred<T> {
/**
* Resolve this deferred's promise with a value.
*
* @param value The value to resolve the promise with.
*/
resolve(value?: T): void;
/**
* Reject this deferred with an error.
*
* @param value The reason for rejecting the promise.
*/
reject(reason?: any): void;
/**
* The backing promise.
*
* @see MithrilPromise
*/
promise: MithrilPromise<T>;
}
/**
* This represents a thennable success callback.
*/
interface MithrilSuccessCallback<T, U> {
(value: T): U | Thennable<U>;
}
/**
* This represents a thennable error callback.
*/
interface MithrilErrorCallback<T> {
(value: Error): T | Thennable<T>;
}
/**
* This represents a thennable.
*/
interface Thennable<T> {
then<U>(success: (value: T) => U): Thennable<U>;
then<U,V>(success: (value: T) => U, error: (value: Error) => V): Thennable<U>|Thennable<V>;
catch?: <U>(error: (value: Error) => U) => Thennable<U>;
}
/**
* This represents a Mithril promise object.
*/
interface MithrilPromise<T> extends Thennable<T>, MithrilProperty<MithrilPromise<T>> {
/**
* Chain this promise with a simple success callback, propogating
* rejections.
*
* @param success The callback to call when the promise is resolved.
* @return The chained promise.
*/
then<U>(success: MithrilSuccessCallback<T,U>): MithrilPromise<U>;
/**
* Chain this promise with a success callback and error callback, without
* propogating rejections.
*
* @param success The callback to call when the promise is resolved.
* @param error The callback to call when the promise is rejected.
* @return The chained promise.
*/
then<U, V>(
success: MithrilSuccessCallback<T, U>,
error: MithrilErrorCallback<V>
): MithrilPromise<U> | MithrilPromise<V>;
/**
* Chain this promise with a single error callback, without propogating
* rejections.
*
* @param error The callback to call when the promise is rejected.
* @return The chained promise.
*/
catch<U>(error: MithrilErrorCallback<U>): MithrilPromise<T> |
MithrilPromise<U>;
}
/**
* This represents the available options for configuring m.request.
*
* @see m.request
*/
interface MithrilXHROptions<T> {
/**
* This represents the HTTP method used, one of the following:
*
* - "GET" (default)
* - "POST"
* - "PUT"
* - "DELETE"
* - "HEAD"
* - "OPTIONS"
*/
method?: string;
/**
* The URL to send the request to.
*/
url: string;
/**
* The username for HTTP authentication.
*/
user?: string;
/**
* The password for HTTP authentication.
*/
password?: string;
/**
* The data to be sent. It's automatically serialized in the right format
* depending on the method (with exception of HTML5 FormData), and put in
* the appropriate section of the request.
*/
data?: any;
/**
* Whether to run it in the background, i.e. true if it doesn't affect
* template rendering.
*/
background?: boolean;
/**
* Set an initial value while the request is working, to populate the
* promise getter-setter.
*/
initialValue?: T;
/**
* An optional preprocessor function to unwrap a successful response, in
* case the response contains metadata wrapping the data.
*
* @param data The data to unwrap.
* @return The unwrapped result.
*/
unwrapSuccess?(data: any): T;
/**
* An optional preprocessor function to unwrap an unsuccessful response,
* in case the response contains metadata wrapping the data.
*
* @param data The data to unwrap.
* @return The unwrapped result.
*/
unwrapError?(data: any): T;
/**
* An optional function to serialize the data. This defaults to
* `JSON.stringify`.
*
* @param dataToSerialize The data to serialize.
* @return The serialized form as a string.
*/
serialize?(dataToSerialize: any): string;
/**
* An optional function to deserialize the data. This defaults to
* `JSON.parse`.
*
* @param dataToSerialize The data to parse.
* @return The parsed form.
*/
deserialize?(dataToDeserialize: string): any;
/**
* An optional function to extract the data from a raw XMLHttpRequest,
* useful if the relevant data is in a response header or the status
* field.
*
* @param xhr The associated XMLHttpRequest.
* @param options The options passed to this request.
* @return string The serialized format.
*/
extract?(xhr: XMLHttpRequest, options: MithrilXHROptions<T>): string;
/**
* The parsed data, or its children if it's an array, will be passed to
* this class constructor if it's given, to parse it into classes.
*
* @param data The data to parse.
* @return The new instance for the list.
*/
type?: new (data: Object) => any;
/**
* An optional function to run between `open` and `send`, useful for
* adding request headers or using XHR2 features such as the `upload`
* property. It is even possible to override the XHR altogether with a
* similar object, such as an XDomainRequest instance.
*
* @param xhr The associated XMLHttpRequest.
* @param options The options passed to this request.
* @return The new XMLHttpRequest, or nothing if the same one is kept.
*/
config?(xhr: XMLHttpRequest, options: MithrilXHROptions<T>): any;
/**
* For JSONP requests, this must be the string "jsonp". Otherwise, it's
* ignored.
*/
dataType?: string;
/**
* For JSONP requests, this is the query string key for the JSONP
* request. This is useful for APIs that don't use common conventions,
* such as `www.example.com/?jsonpCallback=doSomething`. It defaults to
* `callback` for JSONP requests, and is ignored for any other kind of
* request.
*/
callbackKey?: string;
}
}
declare var Mithril: _mithril.MithrilStatic;
declare var m: _mithril.MithrilStatic;
declare module "mithril" {
export = m;
}

View File

@ -32,16 +32,6 @@ System.config({
defaultJSExtensions: true, defaultJSExtensions: true,
}); });
// Register mithril as a module,
// but only if it is ambient.
if (typeof m !== "undefined") {
let mod = System.newModule({default: m});
let modName = "mithril";
System.set(modName, mod);
}
let me = window.location.protocol let me = window.location.protocol
+ "//" + window.location.host + "//" + window.location.host
+ window.location.pathname.replace(/[.]html$/, ".js"); + window.location.pathname.replace(/[.]html$/, ".js");

View File

@ -1,29 +0,0 @@
/**
* @license
* lodash 4.0.0 (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
* Build: `lodash core -o ./dist/lodash.core.js`
*/
;(function(){function n(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];return n}function t(n,t,r){for(var e=-1,u=n.length;++e<u;){var o=n[e],i=t(o);if(null!=i&&(c===ln?i===i:r(i,c)))var c=i,f=o}return f}function r(n,t,r){var e;return r(n,function(n,r,u){return t(n,r,u)?(e=n,false):void 0}),e}function e(n,t,r,e,u){return u(n,function(n,u,o){r=e?(e=false,n):t(r,n,u,o)}),r}function u(n,t){return w(t,function(t){return n[t]})}function o(n){return n&&n.Object===Object?n:null}function i(n){return vn[n];
}function c(n){var t=false;if(null!=n&&typeof n.toString!="function")try{t=!!(n+"")}catch(r){}return t}function f(n,t){return n=typeof n=="number"||hn.test(n)?+n:-1,n>-1&&0==n%1&&(null==t?9007199254740991:t)>n}function a(n){if(Z(n)&&!Vn(n)){if(n instanceof l)return n;if(En.call(n,"__wrapped__")){var t=new l(n.__wrapped__,n.__chain__);return t.__actions__=k(n.__actions__),t}}return new l(n)}function l(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t}function p(n,t,r,e){return n===ln||H(n,xn[r])&&!En.call(e,r)?t:n;
}function s(n,t,r){if(typeof n!="function")throw new TypeError("Expected a function");return setTimeout(function(){n.apply(ln,r)},t)}function h(n,t){var r=true;return $n(n,function(n,e,u){return r=!!t(n,e,u)}),r}function v(n,t){var r=[];return $n(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function y(t,r,e,u){u||(u=[]);for(var o=-1,i=t.length;++o<i;){var c=t[o];Z(c)&&Q(c)&&(e||Vn(c)||L(c))?r?y(c,r,e,u):n(u,c):e||(u[u.length]=c)}return u}function _(n,t){return n&&qn(n,t,un)}function g(n,t){return v(t,function(t){
return W(n[t])})}function b(n,t,r,e,u){return n===t?true:null==n||null==t||!Y(n)&&!Z(t)?n!==n&&t!==t:j(n,t,b,r,e,u)}function j(n,t,r,e,u,o){var i=Vn(n),f=Vn(t),a="[object Array]",l="[object Array]";i||(a=kn.call(n),"[object Arguments]"==a&&(a="[object Object]")),f||(l=kn.call(t),"[object Arguments]"==l&&(l="[object Object]"));var p="[object Object]"==a&&!c(n),f="[object Object]"==l&&!c(t);return!(l=a==l)||i||p?2&u||(a=p&&En.call(n,"__wrapped__"),f=f&&En.call(t,"__wrapped__"),!a&&!f)?l?(o||(o=[]),(a=C(o,function(t){
return t[0]===n}))&&a[1]?a[1]==t:(o.push([n,t]),t=(i?R:$)(n,t,r,e,u,o),o.pop(),t)):false:r(a?n.value():n,f?t.value():t,e,u,o):I(n,t,a)}function d(n){var t=typeof n;return"function"==t?n:null==n?fn:("object"==t?O:E)(n)}function m(n){n=null==n?n:Object(n);var t,r=[];for(t in n)r.push(t);return r}function w(n,t){var r=-1,e=Q(n)?Array(n.length):[];return $n(n,function(n,u,o){e[++r]=t(n,u,o)}),e}function O(n){var t=un(n),r=t.length;return function(e){if(null==e)return!r;for(e=Object(e);r--;){var u=t[r];if(!(u in e&&b(n[u],e[u],ln,true)))return false;
}return true}}function x(n,t){return n=Object(n),J(t,function(t,r){return r in n&&(t[r]=n[r]),t},{})}function E(n){return function(t){return null==t?ln:t[n]}}function A(n,t,r){var e=-1,u=n.length;for(0>t&&(t=-t>u?0:u+t),r=r>u?u:r,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=Array(u);++e<u;)r[e]=n[e+t];return r}function k(n){return A(n,0,n.length)}function N(n,t){var r;return $n(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function S(t,r){return J(r,function(t,r){return r.func.apply(r.thisArg,n([t],r.args))},t);
}function T(n,t,r,e){r||(r={});for(var u=-1,o=t.length;++u<o;){var i=t[u],c=e?e(r[i],n[i],i,r,n):n[i],f=r,a=f[i];(!H(a,c)||H(a,xn[i])&&!En.call(f,i)||c===ln&&!(i in f))&&(f[i]=c)}return r}function F(n){return V(function(t,r){var e=-1,u=r.length,o=u>1?r[u-1]:ln,o=typeof o=="function"?(u--,o):ln;for(t=Object(t);++e<u;){var i=r[e];i&&n(t,i,o)}return t})}function B(n){return function(){var t=arguments,r=In(n.prototype),t=n.apply(r,t);return Y(t)?t:r}}function D(n,t,r){function e(){for(var o=-1,i=arguments.length,c=-1,f=r.length,a=Array(f+i),l=this&&this!==wn&&this instanceof e?u:n;++c<f;)a[c]=r[c];
for(;i--;)a[c++]=arguments[++o];return l.apply(t,a)}if(typeof n!="function")throw new TypeError("Expected a function");var u=B(n);return e}function R(n,t,r,e,u,o){var i=-1,c=1&u,f=n.length,a=t.length;if(f!=a&&!(2&u&&a>f))return false;for(a=true;++i<f;){var l=n[i],p=t[i];if(void 0!==ln){a=false;break}if(c){if(!N(t,function(n){return l===n||r(l,n,e,u,o)})){a=false;break}}else if(l!==p&&!r(l,p,e,u,o)){a=false;break}}return a}function I(n,t,r){switch(r){case"[object Boolean]":case"[object Date]":return+n==+t;case"[object Error]":
return n.name==t.name&&n.message==t.message;case"[object Number]":return n!=+n?t!=+t:n==+t;case"[object RegExp]":case"[object String]":return n==t+""}return false}function $(n,t,r,e,u,o){var i=2&u,c=1&u,f=un(n),a=f.length,l=un(t);if(a!=l.length&&!i)return false;for(var p=a;p--;){var s=f[p];if(!(i?s in t:En.call(t,s))||!c&&s!=l[p])return false}for(c=true;++p<a;){var s=f[p],l=n[s],h=t[s];if(void 0!==ln||l!==h&&!r(l,h,e,u,o)){c=false;break}i||(i="constructor"==s)}return c&&!i&&(r=n.constructor,e=t.constructor,r!=e&&"constructor"in n&&"constructor"in t&&!(typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)&&(c=false)),
c}function q(n){var t=n?n.length:ln;if(X(t)&&(Vn(n)||tn(n)||L(n))){n=String;for(var r=-1,e=Array(t);++r<t;)e[r]=n(r);t=e}else t=null;return t}function M(n){var t=n&&n.constructor;return n===(typeof t=="function"&&t.prototype||xn)}function z(n){return n?n[0]:ln}function C(n,t){return r(n,d(t),$n)}function G(n,t){return $n(n,typeof t=="function"?t:fn)}function J(n,t,r){return e(n,d(t),r,3>arguments.length,$n)}function P(n){return null==n?0:(n=Q(n)?n:un(n),n.length)}function U(n,t){var r;if(typeof t!="function")throw new TypeError("Expected a function");
return n=Hn(n),function(){return 0<--n&&(r=t.apply(this,arguments)),1>=n&&(t=ln),r}}function V(n){var t;if(typeof n!="function")throw new TypeError("Expected a function");return t=Rn(t===ln?n.length-1:Hn(t),0),function(){for(var r=arguments,e=-1,u=Rn(r.length-t,0),o=Array(u);++e<u;)o[e]=r[t+e];for(u=Array(t+1),e=-1;++e<t;)u[e]=r[e];return u[t]=o,n.apply(this,u)}}function H(n,t){return n===t||n!==n&&t!==t}function K(n,t){return n>t}function L(n){return Z(n)&&Q(n)&&En.call(n,"callee")&&(!Fn.call(n,"callee")||"[object Arguments]"==kn.call(n));
}function Q(n){return null!=n&&!(typeof n=="function"&&W(n))&&X(Mn(n))}function W(n){return n=Y(n)?kn.call(n):"","[object Function]"==n||"[object GeneratorFunction]"==n}function X(n){return typeof n=="number"&&n>-1&&0==n%1&&9007199254740991>=n}function Y(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}function Z(n){return!!n&&typeof n=="object"}function nn(n){return typeof n=="number"||Z(n)&&"[object Number]"==kn.call(n)}function tn(n){return typeof n=="string"||!Vn(n)&&Z(n)&&"[object String]"==kn.call(n);
}function rn(n,t){return t>n}function en(n){return typeof n=="string"?n:null==n?"":n+""}function un(n){var t=M(n);if(!t&&!Q(n))return Dn(Object(n));var r,e=q(n),u=!!e,e=e||[],o=e.length;for(r in n)!En.call(n,r)||u&&("length"==r||f(r,o))||t&&"constructor"==r||e.push(r);return e}function on(n){for(var t=-1,r=M(n),e=m(n),u=e.length,o=q(n),i=!!o,o=o||[],c=o.length;++t<u;){var a=e[t];i&&("length"==a||f(a,c))||"constructor"==a&&(r||!En.call(n,a))||o.push(a)}return o}function cn(n){return n?u(n,un(n)):[];
}function fn(n){return n}function an(t,r,e){var u=un(r),o=g(r,u);null!=e||Y(r)&&(o.length||!u.length)||(e=r,r=t,t=this,o=g(r,un(r)));var i=Y(e)&&"chain"in e?e.chain:true,c=W(t);return $n(o,function(e){var u=r[e];t[e]=u,c&&(t.prototype[e]=function(){var r=this.__chain__;if(i||r){var e=t(this.__wrapped__);return(e.__actions__=k(this.__actions__)).push({func:u,args:arguments,thisArg:t}),e.__chain__=r,e}return u.apply(t,n([this.value()],arguments))})}),t}var ln,pn=/[&<>"'`]/g,sn=RegExp(pn.source),hn=/^(?:0|[1-9]\d*)$/,vn={
"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","`":"&#96;"},yn={"function":true,object:true},_n=yn[typeof exports]&&exports&&!exports.nodeType?exports:null,gn=yn[typeof module]&&module&&!module.nodeType?module:null,bn=o(yn[typeof self]&&self),jn=o(yn[typeof window]&&window),dn=gn&&gn.exports===_n?_n:null,mn=o(yn[typeof this]&&this),wn=o(_n&&gn&&typeof global=="object"&&global)||jn!==(mn&&mn.window)&&jn||bn||mn||Function("return this")(),On=Array.prototype,xn=Object.prototype,En=xn.hasOwnProperty,An=0,kn=xn.toString,Nn=wn._,Sn=wn.f,Tn=Sn?Sn.g:ln,Fn=xn.propertyIsEnumerable,Bn=wn.isFinite,Dn=Object.keys,Rn=Math.max,In=function(){
function n(){}return function(t){if(Y(t)){n.prototype=t;var r=new n;n.prototype=ln}return r||{}}}(),$n=function(n,t){return function(r,e){if(null==r)return r;if(!Q(r))return n(r,e);for(var u=r.length,o=t?u:-1,i=Object(r);(t?o--:++o<u)&&false!==e(i[o],o,i););return r}}(_),qn=function(n){return function(t,r,e){var u=-1,o=Object(t);e=e(t);for(var i=e.length;i--;){var c=e[n?i:++u];if(false===r(o[c],c,o))break}return t}}();Tn&&!Fn.call({valueOf:1},"valueOf")&&(m=function(n){n=Tn(n);for(var t,r=[];!(t=n.next()).done;)r.push(t.value);
return r});var Mn=E("length"),zn=V(function(t,r){y(r);var e=Vn(t)?t:[Object(t)];return n(k(e),cn)}),Cn=V(function(n,t,r){var e=typeof t=="function";return w(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})}),Gn=Date.now,Jn=V(function(n,t,r){return D(n,t,r)}),Pn=V(function(n,t){return s(n,1,t)}),Un=V(function(n,t,r){return s(n,Kn(t)||0,r)}),Vn=Array.isArray,Hn=Number,Kn=Number,Ln=F(function(n,t){T(t,un(t),n)}),Qn=F(function(n,t){T(t,on(t),n)}),Wn=F(function(n,t,r){T(t,on(t),n,r)}),Xn=V(function(n){
return n.push(ln,p),Wn.apply(ln,n)}),Yn=V(function(n,t){return null==n?{}:x(n,y(t))}),Zn=d;l.prototype=In(a.prototype),l.prototype.constructor=l,a.assignIn=Qn,a.before=U,a.bind=Jn,a.chain=function(n){return n=a(n),n.__chain__=true,n},a.compact=function(n){return v(n,Boolean)},a.concat=zn,a.create=function(n,t){var r=In(n);return t?Ln(r,t):r},a.defaults=Xn,a.defer=Pn,a.delay=Un,a.filter=function(n,t){return v(n,d(t))},a.flatten=function(n){return n&&n.length?y(n):[]},a.flattenDeep=function(n){return n&&n.length?y(n,true):[];
},a.invokeMap=Cn,a.iteratee=Zn,a.keys=un,a.map=function(n,t){return w(n,d(t))},a.mixin=an,a.negate=function(n){if(typeof n!="function")throw new TypeError("Expected a function");return function(){return!n.apply(this,arguments)}},a.once=function(n){return U(2,n)},a.pick=Yn,a.slice=function(n,t,r){return n&&n.length?A(n,t,r):[]},a.sortBy=function(n,t){var r=0;return t=d(t),w(w(n,function(n,e,u){return{c:n,b:r++,a:t(n,e,u)}}).sort(function(n,t){var r;n:{r=n.a;var e=t.a;if(r!==e){var u=null===r,o=r===ln,i=r===r,c=null===e,f=e===ln,a=e===e;
if(r>e&&!c||!i||u&&!f&&a||o&&a){r=1;break n}if(e>r&&!u||!a||c&&!o&&i||f&&i){r=-1;break n}}r=0}return r||n.b-t.b}),E("c"))},a.tap=function(n,t){return t(n),n},a.thru=function(n,t){return t(n)},a.toArray=function(n){return Q(n)?n.length?k(n):[]:cn(n)},a.values=cn,a.each=G,a.extend=Qn,an(a,a),a.clone=function(n){return Y(n)?Vn(n)?k(n):T(n,un(n)):n},a.escape=function(n){return(n=en(n))&&sn.test(n)?n.replace(pn,i):n},a.every=function(n,t,r){return t=r?ln:t,h(n,d(t))},a.find=C,a.forEach=G,a.has=function(n,t){
return null!=n&&En.call(n,t)},a.head=z,a.identity=fn,a.indexOf=function(n,t,r){var e=n?n.length:0;r=typeof r=="number"?0>r?Rn(e+r,0):r:0,r=(r||0)-1;for(var u=t===t;++r<e;){var o=n[r];if(u?o===t:o!==o)return r}return-1},a.isArguments=L,a.isArray=Vn,a.isBoolean=function(n){return true===n||false===n||Z(n)&&"[object Boolean]"==kn.call(n)},a.isDate=function(n){return Z(n)&&"[object Date]"==kn.call(n)},a.isEmpty=function(n){return!Z(n)||W(n.splice)?!P(n):!un(n).length},a.isEqual=function(n,t){return b(n,t)},
a.isFinite=function(n){return typeof n=="number"&&Bn(n)},a.isFunction=W,a.isNaN=function(n){return nn(n)&&n!=+n},a.isNull=function(n){return null===n},a.isNumber=nn,a.isObject=Y,a.isRegExp=function(n){return Y(n)&&"[object RegExp]"==kn.call(n)},a.isString=tn,a.isUndefined=function(n){return n===ln},a.last=function(n){var t=n?n.length:0;return t?n[t-1]:ln},a.max=function(n){return n&&n.length?t(n,fn,K):ln},a.min=function(n){return n&&n.length?t(n,fn,rn):ln},a.noConflict=function(){return wn._=Nn,this;
},a.noop=function(){},a.now=Gn,a.reduce=J,a.result=function(n,t,r){return t=null==n?ln:n[t],t===ln&&(t=r),W(t)?t.call(n):t},a.size=P,a.some=function(n,t,r){return t=r?ln:t,N(n,d(t))},a.uniqueId=function(n){var t=++An;return en(n)+t},a.first=z,an(a,function(){var n={};return _(a,function(t,r){En.call(a.prototype,r)||(n[r]=t)}),n}(),{chain:false}),a.VERSION="4.0.0",$n("pop join replace reverse split push shift sort splice unshift".split(" "),function(n){var t=(/^(?:replace|split)$/.test(n)?String.prototype:On)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|join|replace|shift)$/.test(n);
a.prototype[n]=function(){var n=arguments;return e&&!this.__chain__?t.apply(this.value(),n):this[r](function(r){return t.apply(r,n)})}}),a.prototype.toJSON=a.prototype.valueOf=a.prototype.value=function(){return S(this.__wrapped__,this.__actions__)},(jn||bn||{})._=a,typeof define=="function"&&typeof define.amd=="object"&&define.amd? define(function(){return a}):_n&&gn?(dn&&((gn.exports=a)._=a),_n._=a):wn._=a}).call(this);

2233
lib/vendor/mithril.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,6 @@
*/ */
"use strict"; "use strict";
import Dictionary = _.Dictionary;
/** /**
* Declarations and helpers for * Declarations and helpers for
@ -88,7 +87,7 @@ export function exportDb(db: IDBDatabase): Promise<any> {
let dump = { let dump = {
name: db.name, name: db.name,
version: db.version, version: db.version,
stores: {} as Dictionary<any>, stores: {} as {[s: string]: any},
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -99,7 +98,7 @@ export function exportDb(db: IDBDatabase): Promise<any> {
}); });
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 = {} as Dictionary<any>; let storeDump = {} as {[s: string]: any};
dump.stores[name] = storeDump; dump.stores[name] = storeDump;
let store = tx.objectStore(name) let store = tx.objectStore(name)
.openCursor() .openCursor()

View File

@ -29,8 +29,8 @@ export function prettyAmount(amount: AmountJson) {
} }
export function renderContract(contract: Contract): JSX.Element { export function renderContract(contract: Contract): JSX.Element {
let merchantName = m("strong", contract.merchant.name); let merchantName = <strong>{contract.merchant.name}</strong>;
let amount = m("strong", prettyAmount(contract.amount)); let amount = <strong>{prettyAmount(contract.amount)}</strong>;
return ( return (
<div> <div>

View File

@ -10,9 +10,7 @@
<link rel="icon" href="../img/icon.png"> <link rel="icon" href="../img/icon.png">
<script src="../lib/vendor/URI.js"></script> <script src="../lib/vendor/URI.js"></script>
<script src="../lib/vendor/mithril.js"></script>
<script src="../lib/vendor/preact.js"></script> <script src="../lib/vendor/preact.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="../lib/i18n.js"></script> <script src="../lib/i18n.js"></script>

View File

@ -181,6 +181,9 @@ export function processFile(sourceFile: ts.SourceFile) {
function processNode(node: ts.Node) { function processNode(node: ts.Node) {
console.log(ts.SyntaxKind[node.kind]);
ts.forEachChild(node, processNode);
return;
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.CallExpression: case ts.SyntaxKind.CallExpression:
{ {

View File

@ -7,18 +7,18 @@
<link rel="stylesheet" type="text/css" href="../style/lang.css"> <link rel="stylesheet" type="text/css" href="../style/lang.css">
<link rel="stylesheet" type="text/css" href="popup.css"> <link rel="stylesheet" type="text/css" href="popup.css">
<script src="../lib/vendor/mithril.js"></script> <script src="../lib/vendor/preact.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/jed.js"></script> <script src="../lib/vendor/jed.js"></script>
<script src="../lib/i18n.js"></script> <script src="../lib/i18n.js"></script>
<script src="../i18n/strings.js"></script> <script src="../i18n/strings.js"></script>
<script src="../lib/vendor/system-csp-production.src.js"></script>
<script src="../lib/module-trampoline.js"></script> <script src="../lib/module-trampoline.js"></script>
</head> </head>
<body> <body>
<div id="nav"></div> <div id="content"></div>
<div id="content"></div>
</body> </body>
</html> </html>

View File

@ -23,9 +23,6 @@
*/ */
/// <reference path="../lib/decl/mithril.d.ts" />
/// <reference path="../lib/decl/lodash.d.ts" />
"use strict"; "use strict";
import {substituteFulfillmentUrl} from "../lib/wallet/helpers"; import {substituteFulfillmentUrl} from "../lib/wallet/helpers";
@ -33,10 +30,8 @@ import BrowserClickedEvent = chrome.browserAction.BrowserClickedEvent;
import {HistoryRecord, HistoryLevel} from "../lib/wallet/wallet"; import {HistoryRecord, HistoryLevel} from "../lib/wallet/wallet";
import {AmountJson} from "../lib/wallet/types"; import {AmountJson} from "../lib/wallet/types";
declare var m: any;
declare var i18n: any; declare var i18n: any;
function onUpdateNotification(f: () => void) { 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) => {
@ -45,116 +40,198 @@ function onUpdateNotification(f: () => void) {
} }
class Router extends preact.Component<any,any> {
static setRoute(s: string): void {
window.location.hash = s;
preact.rerender();
}
static getRoute(): string {
// Omit the '#' at the beginning
return window.location.hash.substring(1);
}
static onRoute(f: any): () => void {
this.routeHandlers.push(f);
return () => {
let i = this.routeHandlers.indexOf(f);
this.routeHandlers = this.routeHandlers.splice(i, 1);
}
}
static routeHandlers: any[] = [];
componentWillMount() {
console.log("router mounted");
window.onhashchange = () => {
this.forceUpdate();
for (let f of Router.routeHandlers) {
f();
}
}
}
componentWillUnmount() {
console.log("router unmounted");
}
render(props: any, state: any): JSX.Element {
let route = window.location.hash.substring(1);
console.log("rendering route", route);
let defaultChild: JSX.Element|null = null;
for (let child of props.children) {
if (child.attributes["default"]) {
defaultChild = child;
}
if (child.attributes["route"] == route) {
return <div>{child}</div>;
}
}
if (defaultChild == null) {
throw Error("unknown route");
}
console.log("rendering default route");
Router.setRoute(defaultChild.attributes["route"]);
return <div>{defaultChild}</div>;
}
}
export function main() { export function main() {
console.log("popup main"); console.log("popup main");
m.route.mode = "hash";
m.route(document.getElementById("content"), "/balance", { let el = (
"/balance": WalletBalance, <div>
"/history": WalletHistory, <WalletNavBar />
"/debug": WalletDebug, <Router>
}); <WalletBalance route="/balance" default/>
m.mount(document.getElementById("nav"), WalletNavBar); <WalletHistory route="/history"/>
<WalletDebug route="/debug"/>
</Router>
</div>
);
preact.render(el, document.getElementById("content")!);
} }
console.log("this is popup"); interface TabProps extends preact.ComponentProps {
target: string;
}
function Tab(props: TabProps) {
function makeTab(target: string, name: string) {
let cssClass = ""; let cssClass = "";
if (target == m.route()) { if (props.target == Router.getRoute()) {
cssClass = "active"; cssClass = "active";
} }
return m("a", {config: m.route, href: target, "class": cssClass}, name); let onClick = (e: Event) => {
Router.setRoute(props.target);
e.preventDefault();
};
return (
<a onClick={onClick} href={props.target} className={cssClass}>
{props.children}
</a>
);
} }
namespace WalletNavBar {
export function view() { class WalletNavBar extends preact.Component<any,any> {
return m("div#header.nav", [ cancelSubscription: any;
makeTab("/balance", i18n`Balance`),
makeTab("/history", i18n`History`), componentWillMount() {
makeTab("/debug", i18n`Debug`), this.cancelSubscription = Router.onRoute(() => {
]); this.setState({});
});
} }
export function controller() { componentWillUnmount() {
// empty if (this.cancelSubscription) {
this.cancelSubscription();
}
}
render() {
console.log("rendering nav bar");
return (
<div class="nav" id="header">
<Tab target="/balance">
Balance
</Tab>
<Tab target="/history">
History
</Tab>
<Tab target="/debug">
Debug
</Tab>
</div>);
} }
} }
function openInExtension(element: HTMLAnchorElement, isInitialized: boolean) { function ExtensionLink(props: any) {
element.addEventListener("click", (e: Event) => { let onClick = (e: Event) => {
chrome.tabs.create({ chrome.tabs.create({
"url": element.href "url": chrome.extension.getURL(props.target)
}); });
e.preventDefault(); e.preventDefault();
}); };
return (
<a onClick={onClick}>
{props.children}
</a>)
} }
class WalletBalance extends preact.Component<any, any> {
namespace WalletBalance {
export function controller() {
return new Controller();
}
class Controller {
myWallet: any; myWallet: any;
gotError = false; gotError = false;
constructor() { componentWillMount() {
this.updateBalance(); this.updateBalance();
onUpdateNotification(() => this.updateBalance()); onUpdateNotification(() => this.updateBalance());
} }
updateBalance() { updateBalance() {
m.startComputation();
chrome.runtime.sendMessage({type: "balances"}, (resp) => { chrome.runtime.sendMessage({type: "balances"}, (resp) => {
if (resp.error) { if (resp.error) {
this.gotError = true; this.gotError = true;
console.error("could not retrieve balances", resp); console.error("could not retrieve balances", resp);
m.endComputation(); this.forceUpdate();
return; return;
} }
this.gotError = false; this.gotError = false;
console.log("got wallet", resp); console.log("got wallet", resp);
this.myWallet = resp.balances; this.myWallet = resp.balances;
m.endComputation(); this.forceUpdate();
}); });
} }
}
export function view(ctrl: Controller) { render(): JSX.Element {
let wallet = ctrl.myWallet; let wallet = this.myWallet;
if (ctrl.gotError) { if (this.gotError) {
return i18n`Error: could not retrieve balance information.`; return i18n`Error: could not retrieve balance information.`;
} }
if (!wallet) { if (!wallet) {
throw Error("Could not retrieve wallet"); return <div></div>
} }
let listing = _.map(wallet, (x: any) => m("p", formatAmount(x))); console.log(wallet);
let listing = Object.keys(wallet).map((key) => {
return <p>{formatAmount(wallet[key])}</p>
});
if (listing.length > 0) { if (listing.length > 0) {
return listing; return <div>{listing}</div>;
} }
let helpLink = m("a", let helpLink = (
{ <ExtensionLink target="pages/help/empty-wallet.html">
config: openInExtension, help
href: chrome.extension.getURL( </ExtensionLink>
"pages/help/empty-wallet.html") );
},
i18n`help`);
return i18n.parts`You have no balance to show. Need some ${helpLink} getting started?`; return i18n.parts`You have no balance to show. Need some ${helpLink} getting started?`;
} }
} }
function formatTimestamp(t: number) {
let x = new Date(t);
return x.toLocaleString();
}
function formatAmount(amount: AmountJson) { 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}`;
@ -166,17 +243,11 @@ function abbrev(s: string, n: number = 5) {
if (s.length > n) { if (s.length > n) {
sAbbrev = s.slice(0, n) + ".."; sAbbrev = s.slice(0, n) + "..";
} }
return m("span.abbrev", {title: s}, sAbbrev); return (
} <span className="abbrev" title={s}>
{sAbbrev}
</span>
function retryPayment(url: string, contractHash: string) { );
return function() {
chrome.tabs.create({
"url": substituteFulfillmentUrl(url,
{H_contract: contractHash})
});
}
} }
@ -186,78 +257,85 @@ function formatHistoryItem(historyItem: HistoryRecord) {
console.log("hist item", historyItem); console.log("hist item", historyItem);
switch (historyItem.type) { switch (historyItem.type) {
case "create-reserve": case "create-reserve":
return m("p", return (
i18n.parts`Bank requested reserve (${abbrev(d.reservePub)}) for ${formatAmount( <p>
d.requestedAmount)}.`); {i18n.parts`Bank requested reserve (${abbrev(d.reservePub)}) for ${formatAmount(
d.requestedAmount)}.`}
</p>
);
case "confirm-reserve": case "confirm-reserve":
return m("p", return (
i18n.parts`Started to withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( <p>
d.requestedAmount)}.`); {i18n.parts`Started to withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount(
d.requestedAmount)}.`}
</p>
);
case "offer-contract": { case "offer-contract": {
let link = chrome.extension.getURL("view-contract.html"); let link = chrome.extension.getURL("view-contract.html");
let linkElem = m("a", {href: link}, abbrev(d.contractHash)); let linkElem = <a href={link}>{abbrev(d.contractHash)}</a>;
let merchantElem = m("em", abbrev(d.merchantName, 15)); let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
return m("p", return (
i18n.parts`Merchant ${merchantElem} offered contract ${linkElem}.`); <p>
{i18n.parts`Merchant ${merchantElem} offered contract ${linkElem}.`}
</p>
);
} }
case "depleted-reserve": case "depleted-reserve":
return m("p", return (<p>
i18n.parts`Withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount( {i18n.parts`Withdraw from reserve (${abbrev(d.reservePub)}) of ${formatAmount(
d.requestedAmount)} completed.`); d.requestedAmount)} completed.`}
</p>);
case "pay": { case "pay": {
let url = substituteFulfillmentUrl(d.fulfillmentUrl, let url = substituteFulfillmentUrl(d.fulfillmentUrl,
{H_contract: d.contractHash}); {H_contract: d.contractHash});
let merchantElem = m("em", abbrev(d.merchantName, 15)); let merchantElem = <em>{abbrev(d.merchantName, 15)}</em>;
let fulfillmentLinkElem = m(`a`, let fulfillmentLinkElem = <a href={url} onClick={openTab(url)}>view product</a>;
{href: url, onclick: openTab(url)}, return (
"view product"); <p>
return m("p", {i18n.parts`Confirmed payment of ${formatAmount(d.amount)} to merchant ${merchantElem}. (${fulfillmentLinkElem})`}
i18n.parts`Confirmed payment of ${formatAmount(d.amount)} to merchant ${merchantElem}. (${fulfillmentLinkElem})`); </p>);
} }
default: default:
return m("p", i18n`Unknown event (${historyItem.type})`); return (<p>i18n`Unknown event (${historyItem.type})`</p>);
} }
} }
namespace WalletHistory { class WalletHistory extends preact.Component<any, any> {
export function controller() {
return new Controller();
}
class Controller {
myHistory: any[]; myHistory: any[];
gotError = false; gotError = false;
constructor() { componentWillMount() {
this.update(); this.update();
onUpdateNotification(() => this.update()); onUpdateNotification(() => this.update());
} }
update() { update() {
m.startComputation();
chrome.runtime.sendMessage({type: "get-history"}, (resp) => { chrome.runtime.sendMessage({type: "get-history"}, (resp) => {
console.log("got history response");
if (resp.error) { if (resp.error) {
this.gotError = true; this.gotError = true;
console.error("could not retrieve history", resp); console.error("could not retrieve history", resp);
m.endComputation(); this.forceUpdate();
return; return;
} }
this.gotError = false; this.gotError = false;
console.log("got history", resp.history); console.log("got history", resp.history);
this.myHistory = resp.history; this.myHistory = resp.history;
m.endComputation(); this.forceUpdate();
}); });
} }
}
export function view(ctrl: Controller) { render(): JSX.Element {
let history: HistoryRecord[] = ctrl.myHistory; console.log("rendering history");
if (ctrl.gotError) { let history: HistoryRecord[] = this.myHistory;
if (this.gotError) {
return i18n`Error: could not retrieve event history`; return i18n`Error: could not retrieve event history`;
} }
if (!history) { if (!history) {
throw Error("Could not retrieve history"); // We're not ready yet
return <span />;
} }
let subjectMemo: {[s: string]: boolean} = {}; let subjectMemo: {[s: string]: boolean} = {};
@ -271,19 +349,24 @@ namespace WalletHistory {
} }
subjectMemo[record.subjectId as string] = true; subjectMemo[record.subjectId as string] = true;
let item = m("div.historyItem", {}, [ let item = (
m("div.historyDate", {}, (new Date(record.timestamp)).toString()), <div className="historyItem">
formatHistoryItem(record) <div className="historyDate">
]); {(new Date(record.timestamp)).toString()}
</div>
{formatHistoryItem(record)}
</div>
);
listing.push(item); listing.push(item);
} }
if (listing.length > 0) { if (listing.length > 0) {
return m("div.container", listing); return <div className="container">{listing}</div>;
} }
return i18n`Your wallet has no events recorded.`; return <p>{i18n`Your wallet has no events recorded.`}</p>
} }
} }
@ -305,21 +388,24 @@ function confirmReset() {
} }
var WalletDebug = { function WalletDebug(props: any) {
view() { return (<div>
return [ <p>Debug tools:</p>
m("button", <button onClick={openExtensionPage("popup/popup.html")}>
{onclick: openExtensionPage("popup/popup.html")}, wallet tab
"wallet tab"), </button>
m("button", <button onClick={openExtensionPage("pages/show-db.html")}>
{onclick: openExtensionPage("pages/show-db.html")}, show db
"show db"), </button>
m("br"), <br />
m("button", {onclick: confirmReset}, "reset"), <button onClick={confirmReset}>
m("button", {onclick: reload}, "reload chrome extension"), reset
] </button>
} <button onClick={reload}>
}; reload chrome extension
</button>
</div>);
}
function openExtensionPage(page: string) { function openExtensionPage(page: string) {