@screeb/rrweb 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +42 -0
- package/dist/rrweb.cjs +4207 -11039
- package/dist/rrweb.cjs.map +1 -1
- package/dist/rrweb.d.cts +171 -58
- package/dist/rrweb.d.ts +171 -58
- package/dist/rrweb.js +4205 -11038
- package/dist/rrweb.js.map +1 -1
- package/dist/rrweb.umd.cjs +3888 -10586
- package/dist/rrweb.umd.cjs.map +4 -4
- package/dist/rrweb.umd.min.cjs +6 -68
- package/dist/rrweb.umd.min.cjs.map +4 -4
- package/dist/style.min.css +1 -1
- package/dist/style.min.css.map +1 -1
- package/dist/ts3.8/rrweb.d.ts +479 -0
- package/package.json +29 -20
- package/dist/assets/image-bitmap-data-url-worker-IJpC7g_b.js.map +0 -1
package/dist/rrweb.d.ts
CHANGED
|
@@ -1,48 +1,120 @@
|
|
|
1
|
-
import { actionWithDelay } from '@rrweb
|
|
2
|
-
import { addedNodeMutation } from '@rrweb
|
|
3
|
-
import { blockClass } from '@rrweb
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
1
|
+
import { actionWithDelay } from '@sentry-internal/rrweb-types';
|
|
2
|
+
import { addedNodeMutation } from '@sentry-internal/rrweb-types';
|
|
3
|
+
import { blockClass } from '@sentry-internal/rrweb-types';
|
|
4
|
+
import { CanvasArg } from '@sentry-internal/rrweb-types';
|
|
5
|
+
import { canvasMutationCallback } from '@sentry-internal/rrweb-types';
|
|
6
|
+
import { canvasMutationData } from '@sentry-internal/rrweb-types';
|
|
7
|
+
import { canvasMutationParam } from '@sentry-internal/rrweb-types';
|
|
8
|
+
import { DataURLOptions } from '@sentry-internal/rrweb-snapshot';
|
|
9
|
+
import { DeprecatedMirror } from '@sentry-internal/rrweb-types';
|
|
10
|
+
import { DocumentDimension } from '@sentry-internal/rrweb-types';
|
|
11
|
+
import { Emitter } from '@sentry-internal/rrweb-types';
|
|
12
|
+
import { EventType } from '@sentry-internal/rrweb-types';
|
|
13
|
+
import { eventWithTime } from '@sentry-internal/rrweb-types';
|
|
14
|
+
import { fullSnapshotEvent } from '@sentry-internal/rrweb-types';
|
|
15
|
+
import { Handler } from '@sentry-internal/rrweb-types';
|
|
16
|
+
import { hookResetter } from '@sentry-internal/rrweb-types';
|
|
17
|
+
import { hooksParam } from '@sentry-internal/rrweb-types';
|
|
18
|
+
import { IMirror } from '@sentry-internal/rrweb-snapshot';
|
|
19
|
+
import { incrementalSnapshotEvent } from '@sentry-internal/rrweb-types';
|
|
20
|
+
import { IncrementalSource } from '@sentry-internal/rrweb-types';
|
|
21
|
+
import { inputData } from '@sentry-internal/rrweb-types';
|
|
22
|
+
import { IWindow } from '@sentry-internal/rrweb-types';
|
|
23
|
+
import { KeepIframeSrcFn } from '@sentry-internal/rrweb-types';
|
|
24
|
+
import { listenerHandler } from '@sentry-internal/rrweb-types';
|
|
25
|
+
import { MaskAttributeFn } from '@sentry-internal/rrweb-snapshot';
|
|
26
|
+
import { MaskInputFn } from '@sentry-internal/rrweb-snapshot';
|
|
27
|
+
import { MaskInputOptions } from '@sentry-internal/rrweb-snapshot';
|
|
28
|
+
import { maskTextClass } from '@sentry-internal/rrweb-types';
|
|
29
|
+
import { MaskTextFn } from '@sentry-internal/rrweb-snapshot';
|
|
30
|
+
import { Mirror } from '@sentry-internal/rrweb-snapshot';
|
|
31
|
+
import { MouseInteractions } from '@sentry-internal/rrweb-types';
|
|
32
|
+
import { PackFn } from '@sentry-internal/rrweb-types';
|
|
33
|
+
import { playerMetaData } from '@sentry-internal/rrweb-types';
|
|
34
|
+
import { RecordPlugin } from '@sentry-internal/rrweb-types';
|
|
35
|
+
import { ReplayerEvents } from '@sentry-internal/rrweb-types';
|
|
36
|
+
import { RRDocument } from '@sentry-internal/rrdom';
|
|
37
|
+
import { RRIFrameElement } from '@sentry-internal/rrdom';
|
|
38
|
+
import { RRNode } from '@sentry-internal/rrdom';
|
|
39
|
+
import { SamplingStrategy } from '@sentry-internal/rrweb-types';
|
|
40
|
+
import { SlimDOMOptions } from '@sentry-internal/rrweb-snapshot';
|
|
34
41
|
import { StateMachine } from '@xstate/fsm';
|
|
35
|
-
import { textMutation } from '@rrweb
|
|
36
|
-
import { throttleOptions } from '@rrweb
|
|
37
|
-
import {
|
|
42
|
+
import { textMutation } from '@sentry-internal/rrweb-types';
|
|
43
|
+
import { throttleOptions } from '@sentry-internal/rrweb-types';
|
|
44
|
+
import { unmaskTextClass } from '@sentry-internal/rrweb-types';
|
|
45
|
+
import { UnpackFn } from '@sentry-internal/rrweb-types';
|
|
38
46
|
|
|
39
|
-
export declare
|
|
47
|
+
export declare function addCustomEvent<T>(tag: string, payload: T): void;
|
|
40
48
|
|
|
41
49
|
declare type AppendedIframe = {
|
|
42
50
|
mutationInQueue: addedNodeMutation;
|
|
43
51
|
builtNode: HTMLIFrameElement | RRIFrameElement;
|
|
44
52
|
};
|
|
45
53
|
|
|
54
|
+
export declare class CanvasManager implements CanvasManagerInterface {
|
|
55
|
+
private pendingCanvasMutations;
|
|
56
|
+
private rafStamps;
|
|
57
|
+
private options;
|
|
58
|
+
private mirror;
|
|
59
|
+
private shadowDoms;
|
|
60
|
+
private windowsSet;
|
|
61
|
+
private windows;
|
|
62
|
+
private mutationCb;
|
|
63
|
+
private restoreHandlers;
|
|
64
|
+
private frozen;
|
|
65
|
+
private locked;
|
|
66
|
+
private snapshotInProgressMap;
|
|
67
|
+
private worker;
|
|
68
|
+
private lastSnapshotTime;
|
|
69
|
+
reset(): void;
|
|
70
|
+
freeze(): void;
|
|
71
|
+
unfreeze(): void;
|
|
72
|
+
lock(): void;
|
|
73
|
+
unlock(): void;
|
|
74
|
+
constructor(options: CanvasManagerConstructorOptions);
|
|
75
|
+
addWindow(win: IWindow): void;
|
|
76
|
+
addShadowRoot(shadowRoot: ShadowRoot): void;
|
|
77
|
+
resetShadowRoots(): void;
|
|
78
|
+
snapshot(canvasElement?: HTMLCanvasElement, options?: SnapshotOptions): void;
|
|
79
|
+
private initFPSWorker;
|
|
80
|
+
private processMutation;
|
|
81
|
+
private initCanvasFPSObserver;
|
|
82
|
+
private initCanvasMutationObserver;
|
|
83
|
+
private getCanvasElements;
|
|
84
|
+
private takeSnapshot;
|
|
85
|
+
private startPendingCanvasMutationFlusher;
|
|
86
|
+
private startRAFTimestamping;
|
|
87
|
+
flushPendingCanvasMutations(): void;
|
|
88
|
+
flushPendingCanvasMutationFor(canvas: HTMLCanvasElement, id: number): void;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export declare interface CanvasManagerConstructorOptions {
|
|
92
|
+
recordCanvas: boolean;
|
|
93
|
+
enableManualSnapshot?: boolean;
|
|
94
|
+
mutationCb: canvasMutationCallback;
|
|
95
|
+
win: IWindow;
|
|
96
|
+
blockClass: blockClass;
|
|
97
|
+
blockSelector: string | null;
|
|
98
|
+
unblockSelector: string | null;
|
|
99
|
+
maxCanvasSize?: MaxCanvasSize | null;
|
|
100
|
+
mirror: Mirror;
|
|
101
|
+
dataURLOptions: DataURLOptions;
|
|
102
|
+
errorHandler?: ErrorHandler;
|
|
103
|
+
sampling?: 'all' | number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
declare interface CanvasManagerInterface {
|
|
107
|
+
reset(): void;
|
|
108
|
+
freeze(): void;
|
|
109
|
+
unfreeze(): void;
|
|
110
|
+
lock(): void;
|
|
111
|
+
unlock(): void;
|
|
112
|
+
snapshot(canvasElement?: HTMLCanvasElement, options?: SnapshotOptions): void;
|
|
113
|
+
addWindow(win: IWindow): void;
|
|
114
|
+
addShadowRoot(shadowRoot: ShadowRoot): void;
|
|
115
|
+
resetShadowRoots(): void;
|
|
116
|
+
}
|
|
117
|
+
|
|
46
118
|
export declare function canvasMutation({ event, mutation, target, imageMap, canvasEventMap, errorHandler, }: {
|
|
47
119
|
event: Parameters<Replayer['applyIncremental']>[0];
|
|
48
120
|
mutation: canvasMutationData;
|
|
@@ -52,19 +124,31 @@ export declare function canvasMutation({ event, mutation, target, imageMap, canv
|
|
|
52
124
|
errorHandler: Replayer['warnCanvasMutationFailed'];
|
|
53
125
|
}): Promise<void>;
|
|
54
126
|
|
|
127
|
+
export { canvasMutationData }
|
|
128
|
+
|
|
129
|
+
export { canvasMutationParam }
|
|
130
|
+
|
|
131
|
+
declare function clearTimeout_2(...rest: Parameters<typeof window.clearTimeout>): ReturnType<typeof window.clearTimeout>;
|
|
132
|
+
|
|
55
133
|
declare function closestElementOfNode(node: Node | null): HTMLElement | null;
|
|
56
134
|
|
|
57
135
|
declare function createPlayerService(context: PlayerContext, { getCastFn, applyEventsSynchronously, emitter }: PlayerAssets): StateMachine.Service<PlayerContext, PlayerEvent, PlayerState>;
|
|
58
136
|
|
|
59
137
|
declare function createSpeedService(context: SpeedContext): StateMachine.Service<SpeedContext, SpeedEvent, SpeedState>;
|
|
60
138
|
|
|
139
|
+
export declare function deserializeArg(imageMap: Replayer['imageMap'], ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext | null, preload?: {
|
|
140
|
+
isUnchanged: boolean;
|
|
141
|
+
}): (arg: CanvasArg) => Promise<any>;
|
|
142
|
+
|
|
61
143
|
declare type ErrorHandler = (error: unknown) => void | boolean;
|
|
62
144
|
|
|
63
145
|
export { EventType }
|
|
64
146
|
|
|
65
147
|
export { eventWithTime }
|
|
66
148
|
|
|
67
|
-
export declare
|
|
149
|
+
export declare function freezePage(): void;
|
|
150
|
+
|
|
151
|
+
export { fullSnapshotEvent }
|
|
68
152
|
|
|
69
153
|
declare function getBaseDimension(node: Node, rootIframe: Node): DocumentDimension;
|
|
70
154
|
|
|
@@ -94,15 +178,19 @@ declare function hasShadowRoot<T extends Node | RRNode>(n: T): n is T & {
|
|
|
94
178
|
|
|
95
179
|
declare function hookSetter<T>(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter;
|
|
96
180
|
|
|
181
|
+
export { incrementalSnapshotEvent }
|
|
182
|
+
|
|
97
183
|
export { IncrementalSource }
|
|
98
184
|
|
|
99
185
|
declare function inDom(n: Node): boolean;
|
|
100
186
|
|
|
187
|
+
export { inputData }
|
|
188
|
+
|
|
101
189
|
declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean;
|
|
102
190
|
|
|
103
|
-
declare function isBlocked(node: Node | null, blockClass: blockClass, blockSelector: string | null, checkAncestors: boolean): boolean;
|
|
191
|
+
declare function isBlocked(node: Node | null, blockClass: blockClass, blockSelector: string | null, unblockSelector: string | null, checkAncestors: boolean): boolean;
|
|
104
192
|
|
|
105
|
-
declare function isIgnored(n: Node, mirror: Mirror
|
|
193
|
+
declare function isIgnored(n: Node, mirror: Mirror): boolean;
|
|
106
194
|
|
|
107
195
|
declare function isSerialized(n: Node, mirror: Mirror): boolean;
|
|
108
196
|
|
|
@@ -114,7 +202,9 @@ declare function iterateResolveTree(tree: ResolveTree, cb: (mutation: addedNodeM
|
|
|
114
202
|
|
|
115
203
|
declare function legacy_isTouchEvent(event: MouseEvent | TouchEvent | PointerEvent): event is TouchEvent;
|
|
116
204
|
|
|
117
|
-
|
|
205
|
+
declare type MaxCanvasSize = [number, number];
|
|
206
|
+
|
|
207
|
+
declare let _mirror: DeprecatedMirror;
|
|
118
208
|
|
|
119
209
|
export { MouseInteractions }
|
|
120
210
|
|
|
@@ -122,6 +212,12 @@ declare let nowTimestamp: () => number;
|
|
|
122
212
|
|
|
123
213
|
declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler;
|
|
124
214
|
|
|
215
|
+
declare function onRequestAnimationFrame(...rest: Parameters<typeof requestAnimationFrame>): ReturnType<typeof requestAnimationFrame>;
|
|
216
|
+
|
|
217
|
+
declare function patch(source: {
|
|
218
|
+
[key: string]: any;
|
|
219
|
+
}, name: string, replacement: (...args: unknown[]) => unknown): () => void;
|
|
220
|
+
|
|
125
221
|
declare type PlayerAssets = {
|
|
126
222
|
emitter: Emitter;
|
|
127
223
|
applyEventsSynchronously(events: Array<eventWithTime>): void;
|
|
@@ -194,7 +290,7 @@ declare type PlayerEvent = {
|
|
|
194
290
|
|
|
195
291
|
export declare type PlayerMachineState = StateMachine.State<PlayerContext, PlayerEvent, PlayerState>;
|
|
196
292
|
|
|
197
|
-
declare type PlayerState = {
|
|
293
|
+
export declare type PlayerState = {
|
|
198
294
|
value: 'playing';
|
|
199
295
|
context: PlayerContext;
|
|
200
296
|
} | {
|
|
@@ -212,10 +308,8 @@ declare function queueToResolveTrees(queue: addedNodeMutation[]): ResolveTree[];
|
|
|
212
308
|
export declare function record<T = eventWithTime>(options?: recordOptions<T>): listenerHandler | undefined;
|
|
213
309
|
|
|
214
310
|
export declare namespace record {
|
|
215
|
-
var addCustomEvent: <T>(tag: string, payload: T) => void;
|
|
216
|
-
var freezePage: () => void;
|
|
217
|
-
var takeFullSnapshot: (isCheckout?: boolean | undefined) => void;
|
|
218
311
|
var mirror: Mirror;
|
|
312
|
+
var takeFullSnapshot: takeFullSnapshot;
|
|
219
313
|
}
|
|
220
314
|
|
|
221
315
|
export declare type recordOptions<T> = {
|
|
@@ -224,14 +318,20 @@ export declare type recordOptions<T> = {
|
|
|
224
318
|
checkoutEveryNms?: number;
|
|
225
319
|
blockClass?: blockClass;
|
|
226
320
|
blockSelector?: string;
|
|
321
|
+
unblockSelector?: string;
|
|
227
322
|
ignoreClass?: string;
|
|
228
323
|
ignoreSelector?: string;
|
|
324
|
+
maskAllText?: boolean;
|
|
229
325
|
maskTextClass?: maskTextClass;
|
|
326
|
+
unmaskTextClass?: unmaskTextClass;
|
|
230
327
|
maskTextSelector?: string;
|
|
328
|
+
unmaskTextSelector?: string;
|
|
231
329
|
maskAllInputs?: boolean;
|
|
232
330
|
maskInputOptions?: MaskInputOptions;
|
|
331
|
+
maskAttributeFn?: MaskAttributeFn;
|
|
233
332
|
maskInputFn?: MaskInputFn;
|
|
234
333
|
maskTextFn?: MaskTextFn;
|
|
334
|
+
maxCanvasSize?: [number, number];
|
|
235
335
|
slimDOMOptions?: SlimDOMOptions | 'all' | true;
|
|
236
336
|
ignoreCSSAttributes?: Set<string>;
|
|
237
337
|
inlineStylesheet?: boolean;
|
|
@@ -251,6 +351,8 @@ export declare type recordOptions<T> = {
|
|
|
251
351
|
mousemoveWait?: number;
|
|
252
352
|
keepIframeSrcFn?: KeepIframeSrcFn;
|
|
253
353
|
errorHandler?: ErrorHandler;
|
|
354
|
+
onMutation?: (mutations: MutationRecord[]) => boolean;
|
|
355
|
+
getCanvasManager?: (options: CanvasManagerConstructorOptions) => CanvasManagerInterface;
|
|
254
356
|
};
|
|
255
357
|
|
|
256
358
|
export declare class Replayer {
|
|
@@ -262,9 +364,6 @@ export declare class Replayer {
|
|
|
262
364
|
config: playerConfig;
|
|
263
365
|
usingVirtualDom: boolean;
|
|
264
366
|
virtualDom: RRDocument;
|
|
265
|
-
private mouse;
|
|
266
|
-
private mouseTail;
|
|
267
|
-
private tailPositions;
|
|
268
367
|
private emitter;
|
|
269
368
|
private nextUserInteractionEvent;
|
|
270
369
|
private legacy_missingNodeRetryMap;
|
|
@@ -273,17 +372,16 @@ export declare class Replayer {
|
|
|
273
372
|
private canvasEventMap;
|
|
274
373
|
private mirror;
|
|
275
374
|
private styleMirror;
|
|
276
|
-
private mediaManager;
|
|
277
375
|
private firstFullSnapshot;
|
|
278
376
|
private newDocumentQueue;
|
|
279
|
-
private
|
|
280
|
-
private touchActive;
|
|
377
|
+
private pointers;
|
|
281
378
|
private lastMouseDownEvent;
|
|
282
379
|
private lastHoveredRootNode;
|
|
283
380
|
private lastSelectionData;
|
|
284
381
|
private constructedStyleMutations;
|
|
285
382
|
private adoptedStyleSheets;
|
|
286
383
|
constructor(events: Array<eventWithTime | string>, config?: Partial<playerConfig>);
|
|
384
|
+
private createPointer;
|
|
287
385
|
on(event: string, handler: Handler): this;
|
|
288
386
|
off(event: string, handler: Handler): this;
|
|
289
387
|
setConfig(config: Partial<playerConfig>): void;
|
|
@@ -300,6 +398,7 @@ export declare class Replayer {
|
|
|
300
398
|
enableInteract(): void;
|
|
301
399
|
disableInteract(): void;
|
|
302
400
|
resetCache(): void;
|
|
401
|
+
private reevaluateFastForward;
|
|
303
402
|
private setupDom;
|
|
304
403
|
private handleResize;
|
|
305
404
|
private applyEventsSynchronously;
|
|
@@ -355,8 +454,14 @@ declare type ResolveTree = {
|
|
|
355
454
|
parent: ResolveTree | null;
|
|
356
455
|
};
|
|
357
456
|
|
|
457
|
+
declare function setTimeout_2(...rest: Parameters<typeof window.setTimeout>): ReturnType<typeof window.setTimeout>;
|
|
458
|
+
|
|
358
459
|
declare function shadowHostInDom(n: Node): boolean;
|
|
359
460
|
|
|
461
|
+
declare type SnapshotOptions = {
|
|
462
|
+
skipRequestAnimationFrame?: boolean;
|
|
463
|
+
};
|
|
464
|
+
|
|
360
465
|
declare type SpeedContext = {
|
|
361
466
|
normalSpeed: playerConfig['speed'];
|
|
362
467
|
timer: Timer;
|
|
@@ -378,7 +483,7 @@ declare type SpeedEvent = {
|
|
|
378
483
|
|
|
379
484
|
export declare type SpeedMachineState = StateMachine.State<SpeedContext, SpeedEvent, SpeedState>;
|
|
380
485
|
|
|
381
|
-
declare type SpeedState = {
|
|
486
|
+
export declare type SpeedState = {
|
|
382
487
|
value: 'normal';
|
|
383
488
|
context: SpeedContext;
|
|
384
489
|
} | {
|
|
@@ -398,7 +503,7 @@ declare class StyleSheetMirror {
|
|
|
398
503
|
generateId(): number;
|
|
399
504
|
}
|
|
400
505
|
|
|
401
|
-
export declare
|
|
506
|
+
export declare function takeFullSnapshot(isCheckout?: boolean): void;
|
|
402
507
|
|
|
403
508
|
declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (...args: T[]) => void;
|
|
404
509
|
|
|
@@ -427,6 +532,7 @@ declare namespace utils {
|
|
|
427
532
|
on,
|
|
428
533
|
throttle,
|
|
429
534
|
hookSetter,
|
|
535
|
+
patch,
|
|
430
536
|
getWindowScroll,
|
|
431
537
|
getWindowHeight,
|
|
432
538
|
getWindowWidth,
|
|
@@ -450,7 +556,10 @@ declare namespace utils {
|
|
|
450
556
|
getRootShadowHost,
|
|
451
557
|
shadowHostInDom,
|
|
452
558
|
inDom,
|
|
453
|
-
|
|
559
|
+
onRequestAnimationFrame,
|
|
560
|
+
setTimeout_2 as setTimeout,
|
|
561
|
+
clearTimeout_2 as clearTimeout,
|
|
562
|
+
_mirror,
|
|
454
563
|
nowTimestamp,
|
|
455
564
|
AppendedIframe,
|
|
456
565
|
StyleSheetMirror
|
|
@@ -461,11 +570,15 @@ export { utils }
|
|
|
461
570
|
export { }
|
|
462
571
|
|
|
463
572
|
|
|
573
|
+
declare global {
|
|
574
|
+
const __RRWEB_EXCLUDE_SHADOW_DOM__: boolean;
|
|
575
|
+
const __RRWEB_EXCLUDE_IFRAME__: boolean;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
|
|
464
579
|
declare namespace record {
|
|
465
|
-
var
|
|
466
|
-
var
|
|
467
|
-
var takeFullSnapshot: (isCheckout?: boolean | undefined) => void;
|
|
468
|
-
var mirror: import("rrweb-snapshot").Mirror;
|
|
580
|
+
var mirror: import("@sentry-internal/rrweb-snapshot").Mirror;
|
|
581
|
+
var takeFullSnapshot: typeof import(".").takeFullSnapshot;
|
|
469
582
|
}
|
|
470
583
|
|
|
471
584
|
|