-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathplaceholder.ts
131 lines (123 loc) · 3.92 KB
/
placeholder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { Reactive, State, SListener, BListener, Time } from "./common";
import { Behavior, isBehavior, MapBehavior, pushToChildren } from "./behavior";
import { Node, cons } from "./datastructures";
import { Stream, MapToStream } from "./stream";
import { tick } from "./clock";
import { Future } from "./future";
class SamplePlaceholderError {
message = "Attempt to sample non-replaced placeholder";
constructor(public placeholder: Placeholder<unknown>) {}
toString(): string {
return this.message;
}
}
export class Placeholder<A> extends Behavior<A> {
source: Reactive<A, SListener<A> | BListener>;
private node: Node<this> = new Node(this);
replaceWith(parent: Reactive<A, SListener<A> | BListener>, t?: Time): void {
this.source = parent;
this.parents = cons(parent);
if (this.children.head !== undefined) {
t = t !== undefined ? t : tick();
this.activate(t);
if (isBehavior(parent) && this.state === State.Push) {
pushToChildren(t, this);
}
}
}
pushS(t: number, a: A): void {
for (const child of this.children) {
(child as any).pushS(t, a);
}
}
pull(t: number) {
if (this.source === undefined) {
throw new SamplePlaceholderError(this);
} else if (isBehavior<A>(this.source)) {
this.source.pull(t);
this.pulledAt = t;
this.changedAt = t;
this.last = this.source.last;
} else {
throw new Error("Unsupported pulling on placeholder");
}
}
update(_t: number): A {
return (this.source as Behavior<A>).last;
}
activate(t: number): void {
if (this.source !== undefined) {
this.source.addListener(this.node, t);
if (isBehavior<A>(this.source)) {
this.last = this.source.last;
this.changedAt = this.source.changedAt;
this.pulledAt = this.source.pulledAt;
}
this.changeStateDown(this.source.state);
}
}
deactivate(_done = false): void {
this.state = State.Inactive;
if (this.source !== undefined) {
this.source.removeListener(this.node);
}
}
map<B>(fn: (a: A) => B): Behavior<B> {
return new MapPlaceholder<A, B>(this, fn);
}
mapTo<B>(b: B): Behavior<B> {
return new MapToPlaceholder<A, B>(this as any, b) as any;
}
}
export function isPlaceholder<A>(p: unknown): p is Placeholder<A> {
return typeof p === "object" && "replaceWith" in p;
}
class MapPlaceholder<A, B> extends MapBehavior<A, B> {
pushS(t: number, a: A): void {
// @ts-ignore
this.pushSToChildren(t, this.f(a));
}
}
class MapToPlaceholder<A, B> extends MapToStream<A, B> {
changedAt: Time;
constructor(parent: Stream<A>, public last: B) {
super(parent, last);
}
update(): B {
return this.b;
}
pull(t: Time) {
if (this.changedAt === undefined) {
this.changedAt = t;
}
}
}
function install(target: Function, source: Function): void {
for (const key of Object.getOwnPropertyNames(source.prototype)) {
if (target.prototype[key] === undefined) {
target.prototype[key] = source.prototype[key];
}
}
}
function installMethods(): void {
install(Placeholder, Stream);
install(Placeholder, Future);
MapPlaceholder.prototype.map = Placeholder.prototype.map;
MapPlaceholder.prototype.mapTo = Placeholder.prototype.mapTo;
MapToPlaceholder.prototype.map = Placeholder.prototype.map as any;
MapToPlaceholder.prototype.mapTo = Placeholder.prototype.mapTo as any;
install(MapPlaceholder, Stream);
install(MapPlaceholder, Future);
install(MapToPlaceholder, Behavior);
install(MapPlaceholder, Future);
}
export type PlaceholderObject<A> = Placeholder<A> & Stream<A> & Future<A>;
export function placeholder<A>(): PlaceholderObject<A> {
if ((Placeholder as any).prototype.scanFrom === undefined) {
// The methods are installed lazily when the placeholder is first
// used. This avoids a top-level impure expression that would
// prevent tree-shaking.
installMethods();
}
return new Placeholder() as any;
}