Skip to content

Commit 1db858e

Browse files
committed
build
1 parent 706b004 commit 1db858e

38 files changed

+257
-179
lines changed

demo/index.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/types/base.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ButtonEventNames, ClipboardEventNames, InputEventNames, KeyboardEventNames, PointerEventNames, VideoEventNames, WindowEventNames } from "./ui/events";
1+
import { ButtonEventNames, ClipboardEventNames, DropEventNames, InputEventNames, KeyboardEventNames, PointerEventNames, VideoEventNames, WindowEventNames } from "./ui/events";
22
export declare class AnnotationToolBase<T> {
33
destructors: (() => void)[];
44
isDestroyed: boolean;
@@ -11,6 +11,7 @@ export declare class AnnotationToolBase<T> {
1111
cleanFrameStacks(): void;
1212
destroy(): void;
1313
raf(cb: () => void): number;
14+
addEvent(node: HTMLDivElement, event: DropEventNames, callback: (e: DragEvent) => void): void;
1415
addEvent(node: HTMLInputElement, event: InputEventNames, callback: (e: Event) => void): void;
1516
addEvent(node: typeof document, event: ClipboardEventNames, callback: (e: ClipboardEvent) => void): void;
1617
addEvent(node: typeof document, event: ButtonEventNames, callback: (e: PointerEvent) => void): void;

dist/types/buttons.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { AnnotationTool } from "./core";
2+
type Tool = NonNullable<AnnotationTool["currentTool"]>;
3+
export declare class ButtonConstructor {
4+
tool: AnnotationTool;
5+
uiContainer: HTMLElement;
6+
constructor(tool: AnnotationTool, container: HTMLElement);
7+
get buttons(): HTMLButtonElement[];
8+
get addEvent(): {
9+
(node: HTMLDivElement, event: import("./ui/events").DropEventNames, callback: (e: DragEvent) => void): void;
10+
(node: HTMLInputElement, event: import("./ui/events").InputEventNames, callback: (e: Event) => void): void;
11+
(node: Document, event: import("./ui/events").ClipboardEventNames, callback: (e: ClipboardEvent) => void): void;
12+
(node: Document, event: "click", callback: (e: PointerEvent) => void): void;
13+
(node: HTMLVideoElement, event: import("./ui/events").VideoEventNames, callback: (e: Event) => void): void;
14+
(node: HTMLVideoElement, event: "keydown", callback: (e: KeyboardEvent) => void): void;
15+
(node: HTMLButtonElement, event: "click", callback: (e: Event) => void): void;
16+
(node: HTMLCanvasElement, event: import("./ui/events").PointerEventNames, callback: (e: PointerEvent) => void): void;
17+
(node: Document, event: "keydown", callback: (e: KeyboardEvent) => void): void;
18+
(node: Window & typeof globalThis, event: "resize", callback: (e: Event) => void): void;
19+
};
20+
get currentTool(): AnnotationTool["currentTool"];
21+
set currentTool(value: AnnotationTool["currentTool"]);
22+
create: (icon: string, tool: Tool | ((e: Event) => void), container?: HTMLElement) => HTMLButtonElement;
23+
}
24+
export declare function addButtons(tool: AnnotationTool, Button: ButtonConstructor): void;
25+
export {};

dist/types/core.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export declare class AnnotationTool extends AnnotationToolBase<IShape> {
6969
updateActiveTimeFrame(mediaTime?: number | undefined): void;
7070
show(): void;
7171
setCanvasSettings(): void;
72-
pluginForTool<T extends Tool>(tool: T): ToolPlugin<ShapeMap[T]>;
72+
pluginForTool<T extends keyof ShapeMap>(tool: T): ToolPlugin<ShapeMap[T]>;
7373
getButtonForTool(tool: Tool): HTMLButtonElement;
7474
bindContext(): void;
7575
initProperties(): void;

dist/types/plugins/arrow.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShapeMap } from ".";
12
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
23
export interface IArrow extends IShapeBase {
34
type: "arrow";
@@ -7,7 +8,7 @@ export interface IArrow extends IShapeBase {
78
y2: number;
89
}
910
export declare class ArrowToolPlugin extends BasePlugin<IArrow> implements ToolPlugin<IArrow> {
10-
name: string;
11+
name: keyof ShapeMap;
1112
normalize(shape: IArrow, canvasWidth: number, canvasHeight: number): IArrow;
1213
move(shape: IArrow, dx: number, dy: number): IArrow;
1314
draw(shape: IArrow): void;

dist/types/plugins/audio-peaks.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
22
import { AnnotationTool } from "../core";
33
import type { PeakData } from "webaudio-peaks";
4+
import type { ShapeMap } from ".";
45
export interface IAudioPeaks extends IShapeBase {
56
x: number;
67
y: number;
78
}
89
export declare class AudioPeaksPlugin extends BasePlugin<IAudioPeaks> implements ToolPlugin<IAudioPeaks> {
9-
name: string;
10+
name: keyof ShapeMap;
1011
canvas: HTMLCanvasElement;
1112
drawCtx: CanvasRenderingContext2D;
1213
constructor(annotationTool: AnnotationTool);

dist/types/plugins/base.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import type { ShapeMap } from ".";
12
import type { AnnotationTool } from "./../core";
23
export interface IShapeBase {
3-
type: string;
4+
type: keyof ShapeMap;
45
strokeStyle: string | CanvasGradient | CanvasPattern;
56
fillStyle: string | CanvasPattern | CanvasGradient;
67
lineWidth: number;

dist/types/plugins/circle.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShapeMap } from ".";
12
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
23
export interface ICircle extends IShapeBase {
34
type: "circle";
@@ -6,7 +7,7 @@ export interface ICircle extends IShapeBase {
67
radius: number;
78
}
89
export declare class CircleToolPlugin extends BasePlugin<ICircle> implements ToolPlugin<ICircle> {
9-
name: string;
10+
name: keyof ShapeMap;
1011
move(shape: ICircle, dx: number, dy: number): ICircle;
1112
normalize(shape: ICircle, canvasWidth: number, canvasHeight: number): ICircle;
1213
onPointerDown(event: PointerEvent): void;

dist/types/plugins/compare.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import type { ShapeMap } from ".";
12
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
23
export interface ICompare extends IShapeBase {
34
type: "compare";
45
x: number;
56
disabled: boolean;
67
}
78
export declare class CompareToolPlugin extends BasePlugin<ICompare> implements ToolPlugin<ICompare> {
8-
name: string;
9+
name: keyof ShapeMap;
910
comparisonLine: number;
1011
leftOpacity: number;
1112
rightOpacity: number;

dist/types/plugins/curve.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export type IPoint = {
34
x: number;
45
y: number;
@@ -8,7 +9,7 @@ export interface ICurve extends IShapeBase {
89
points: IPoint[];
910
}
1011
export declare class CurveToolPlugin extends BasePlugin<ICurve> implements ToolPlugin<ICurve> {
11-
name: string;
12+
name: keyof ShapeMap;
1213
curvePoints: IPoint[];
1314
move(shape: ICurve, dx: number, dy: number): ICurve;
1415
normalize(shape: ICurve, canvasWidth: number, canvasHeight: number): ICurve;

dist/types/plugins/eraser.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export interface IEraser extends IShapeBase {
34
type: "eraser";
45
x: number;
@@ -7,7 +8,7 @@ export interface IEraser extends IShapeBase {
78
height: number;
89
}
910
export declare class EraserToolPlugin extends BasePlugin<IEraser> implements ToolPlugin<IEraser> {
10-
name: string;
11+
name: keyof ShapeMap;
1112
move(shape: IEraser, dx: number, dy: number): IEraser;
1213
normalize(shape: IEraser, canvasWidth: number, canvasHeight: number): IEraser;
1314
draw(shape: IEraser): void;

dist/types/plugins/image.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export interface IImage extends IShapeBase {
34
type: "image";
45
image: HTMLImageElement;
@@ -8,7 +9,7 @@ export interface IImage extends IShapeBase {
89
height: number;
910
}
1011
export declare class ImageToolPlugin extends BasePlugin<IImage> implements ToolPlugin<IImage> {
11-
name: string;
12+
name: keyof ShapeMap;
1213
move(shape: IImage, dx: number, dy: number): IImage;
1314
onPointerDown(event: PointerEvent): void;
1415
onPointerMove(event: PointerEvent): void;

dist/types/plugins/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ export interface ShapeMap {
2525
"audio-peaks": IAudioPeaks;
2626
}
2727
export type PluginInstances = RectangleToolPlugin | CircleToolPlugin | LineToolPlugin | ArrowToolPlugin | TextToolPlugin | EraserToolPlugin | CurveToolPlugin | MoveToolPlugin | ImageToolPlugin | CompareToolPlugin | AudioPeaksPlugin;
28-
export declare const plugins: (typeof RectangleToolPlugin | typeof CircleToolPlugin | typeof CurveToolPlugin | typeof LineToolPlugin | typeof ArrowToolPlugin | typeof TextToolPlugin | typeof EraserToolPlugin | typeof ImageToolPlugin | typeof MoveToolPlugin | typeof CompareToolPlugin | typeof AudioPeaksPlugin)[];
28+
export declare const plugins: (typeof RectangleToolPlugin | typeof CircleToolPlugin | typeof CurveToolPlugin | typeof LineToolPlugin | typeof ArrowToolPlugin | typeof TextToolPlugin | typeof EraserToolPlugin | typeof AudioPeaksPlugin | typeof ImageToolPlugin | typeof MoveToolPlugin | typeof CompareToolPlugin)[];

dist/types/plugins/line.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export interface ILine extends IShapeBase {
34
type: "line";
45
x1: number;
@@ -7,7 +8,7 @@ export interface ILine extends IShapeBase {
78
y2: number;
89
}
910
export declare class LineToolPlugin extends BasePlugin<ILine> implements ToolPlugin<ILine> {
10-
name: string;
11+
name: keyof ShapeMap;
1112
move(shape: ILine, dx: number, dy: number): ILine;
1213
normalize(shape: ILine, canvasWidth: number, canvasHeight: number): ILine;
1314
onPointerDown(event: PointerEvent): void;

dist/types/plugins/move.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import type { IShape } from ".";
2+
import { IAudioPeaks } from "./audio-peaks";
23
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
34
import type { IImage } from "./image";
5+
import type { ShapeMap } from ".";
46
export interface IMove extends IShapeBase {
57
type: "move";
68
}
79
export declare class MoveToolPlugin extends BasePlugin<IMove> implements ToolPlugin<IMove> {
8-
name: string;
10+
name: keyof ShapeMap;
911
shape: IShape | null;
1012
lastDrawnShape: IShape | null;
1113
shapeRemoved: boolean;
1214
isScale: boolean;
1315
move(shape: IMove): IMove;
1416
normalize(shape: IMove): IMove;
1517
onPointerDown(event: PointerEvent): void;
16-
isPointerAtCorner(rawShape: IImage, x: number, y: number): boolean;
18+
isPointerAtCorner(rawShape: IImage | IAudioPeaks, x: number, y: number): boolean;
1719
onPointerMove(event: PointerEvent): void;
1820
onPointerUp(event: PointerEvent): void;
1921
draw(): void;

dist/types/plugins/rectangle.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export interface IRectangle extends IShapeBase {
34
type: "rectangle";
45
x: number;
@@ -7,7 +8,7 @@ export interface IRectangle extends IShapeBase {
78
height: number;
89
}
910
export declare class RectangleToolPlugin extends BasePlugin<IRectangle> implements ToolPlugin<IRectangle> {
10-
name: string;
11+
name: keyof ShapeMap;
1112
move(shape: IRectangle, dx: number, dy: number): IRectangle;
1213
normalize(shape: IRectangle, canvasWidth: number, canvasHeight: number): IRectangle;
1314
onPointerDown(event: PointerEvent): void;

dist/types/plugins/text.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23
export interface IText extends IShapeBase {
34
type: "text";
45
x: number;
56
y: number;
67
text: string;
78
}
89
export declare class TextToolPlugin extends BasePlugin<IText> implements ToolPlugin<IText> {
9-
name: string;
10+
name: keyof ShapeMap;
1011
move(shape: IText, dx: number, dy: number): IText;
1112
onActivate(): void;
1213
onDeactivate(): void;

dist/types/ui/color-picker.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import type { AnnotationTool } from "../core";
2+
export declare function createColorPicker(defaultColor: string, tool: AnnotationTool): HTMLInputElement;

dist/types/ui/events.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export type KeyboardEventNames = "keydown";
33
export type ButtonEventNames = "click";
44
export type InputEventNames = "input" | "change";
55
export type ClipboardEventNames = "copy" | "paste" | "cut";
6+
export type DropEventNames = "drop" | "dragover" | "dragenter" | "dragleave";
67
export type VideoEventNames = "timeupdate" | "volumechange" | "seeking" | "play" | "pause" | "seek" | "stalled" | "error" | "requestVideoFrameCallback";
78
export type WindowEventNames = "resize";
8-
export type EventNames = VideoEventNames | InputEventNames | PointerEventNames | KeyboardEventNames | WindowEventNames | ButtonEventNames | ClipboardEventNames;
9+
export type EventNames = VideoEventNames | InputEventNames | PointerEventNames | KeyboardEventNames | WindowEventNames | ButtonEventNames | ClipboardEventNames | DropEventNames;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import type { AnnotationTool } from "../core";
2+
export declare function createStrokeWidthSlider(tool: AnnotationTool): HTMLInputElement;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"type": "module",
1111
"types": "dist/types/index.d.ts",
1212
"devDependencies": {
13-
"esbuild": "^0.17.15",
13+
"esbuild": "0.24.0",
1414
"typescript": "^5.0.4",
1515
"webaudio-peaks": "^1.0.0"
1616
},

src/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ export class AnnotationTool extends AnnotationToolBase<IShape> {
252252
this.ctx.lineWidth = this.selectedStrokeSize;
253253
}
254254

255-
pluginForTool<T extends Tool>(tool: T): ToolPlugin<ShapeMap[T]> {
255+
pluginForTool<T extends keyof ShapeMap>(tool: T): ToolPlugin<ShapeMap[T]> {
256256
if (this.isDestroyed) {
257257
throw new Error("AnnotationTool is destroyed");
258258
}

src/plugins/arrow.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShapeMap } from ".";
12
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
23

34
export interface IArrow extends IShapeBase {
@@ -12,7 +13,7 @@ export class ArrowToolPlugin
1213
extends BasePlugin<IArrow>
1314
implements ToolPlugin<IArrow>
1415
{
15-
name = "arrow";
16+
name = "arrow" as keyof ShapeMap;
1617
normalize(shape: IArrow, canvasWidth: number, canvasHeight: number): IArrow {
1718
return {
1819
...shape,

src/plugins/audio-peaks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
22
import { AnnotationTool } from "../core";
33
import type { PeakData } from "webaudio-peaks";
4+
import type { ShapeMap } from ".";
45

56
export interface IAudioPeaks extends IShapeBase {
67
x: number;
@@ -21,7 +22,7 @@ export class AudioPeaksPlugin
2122
extends BasePlugin<IAudioPeaks>
2223
implements ToolPlugin<IAudioPeaks>
2324
{
24-
name = "audio-peaks";
25+
name = "audio-peaks" as keyof ShapeMap;
2526
canvas = document.createElement("canvas");
2627
drawCtx!: CanvasRenderingContext2D;
2728
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

src/plugins/base.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import type { ShapeMap } from ".";
12
import type { AnnotationTool } from "./../core";
23

34
export interface IShapeBase {
4-
type: string;
5+
type: keyof ShapeMap;
56
strokeStyle: string | CanvasGradient | CanvasPattern;
67
fillStyle: string | CanvasPattern | CanvasGradient;
78
lineWidth: number;

src/plugins/circle.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShapeMap } from ".";
12
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
23

34
export interface ICircle extends IShapeBase {
@@ -11,7 +12,7 @@ export class CircleToolPlugin
1112
extends BasePlugin<ICircle>
1213
implements ToolPlugin<ICircle>
1314
{
14-
name = "circle";
15+
name = "circle" as keyof ShapeMap;
1516
move(shape: ICircle, dx: number, dy: number) {
1617
shape.x += dx;
1718
shape.y += dy;

src/plugins/compare.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ShapeMap } from ".";
12
import { IShapeBase, BasePlugin, ToolPlugin } from "./base";
23

34
export interface ICompare extends IShapeBase {
@@ -12,7 +13,7 @@ export class CompareToolPlugin
1213
extends BasePlugin<ICompare>
1314
implements ToolPlugin<ICompare>
1415
{
15-
name = "compare";
16+
name = "compare" as keyof ShapeMap;
1617
comparisonLine = 0;
1718
leftOpacity = 1;
1819
rightOpacity = 1;

src/plugins/curve.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Point, douglasPeucker } from "./utils/douglas-peucker";
22
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
3+
import type { ShapeMap } from ".";
34

45
export type IPoint = {
56
x: number;
@@ -15,7 +16,7 @@ export class CurveToolPlugin
1516
extends BasePlugin<ICurve>
1617
implements ToolPlugin<ICurve>
1718
{
18-
name = "curve";
19+
name = "curve" as keyof ShapeMap;
1920
curvePoints: IPoint[] = [];
2021
move(shape: ICurve, dx: number, dy: number) {
2122
shape.points = shape.points.map((point) => ({

src/plugins/eraser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23

34
export interface IEraser extends IShapeBase {
45
type: "eraser";
@@ -12,7 +13,7 @@ export class EraserToolPlugin
1213
extends BasePlugin<IEraser>
1314
implements ToolPlugin<IEraser>
1415
{
15-
name = "eraser";
16+
name = "eraser" as keyof ShapeMap;
1617
move(shape: IEraser, dx: number, dy: number) {
1718
shape.x += dx;
1819
shape.y += dy;

src/plugins/image.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BasePlugin, IShapeBase, ToolPlugin } from "./base";
2+
import type { ShapeMap } from ".";
23

34
export interface IImage extends IShapeBase {
45
type: "image";
@@ -13,7 +14,7 @@ export class ImageToolPlugin
1314
extends BasePlugin<IImage>
1415
implements ToolPlugin<IImage>
1516
{
16-
name = "image";
17+
name = "image" as keyof ShapeMap;
1718
move(shape: IImage, dx: number, dy: number) {
1819
shape.x += dx;
1920
shape.y += dy;

src/plugins/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export interface ShapeMap {
3737
image: IImage;
3838
compare: ICompare;
3939
"audio-peaks": IAudioPeaks;
40-
}
40+
};
4141

4242
export type PluginInstances = RectangleToolPlugin | CircleToolPlugin | LineToolPlugin | ArrowToolPlugin | TextToolPlugin | EraserToolPlugin | CurveToolPlugin | MoveToolPlugin | ImageToolPlugin | CompareToolPlugin | AudioPeaksPlugin;
4343

0 commit comments

Comments
 (0)