From fd5c4f18d0af6571508f142ca400e6d752f19800 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 21 Apr 2019 18:41:39 -0400
Subject: Changed WebBox

---
 src/client/views/nodes/WebBox.tsx | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

(limited to 'src/client/views/nodes/WebBox.tsx')

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1edb4d826..e3b6e1c05 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,11 +1,10 @@
 import "./WebBox.scss";
 import React = require("react");
-import { WebField } from '../../../fields/WebField';
 import { FieldViewProps, FieldView } from './FieldView';
-import { FieldWaiting } from '../../../fields/Field';
 import { observer } from "mobx-react";
 import { computed } from 'mobx';
-import { KeyStore } from '../../../fields/KeyStore';
+import { HtmlField } from "../../../new_fields/HtmlField";
+import { URLField } from "../../../new_fields/URLField";
 
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
@@ -16,8 +15,6 @@ export class WebBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-    @computed get html(): string { return this.props.Document.GetHtml(KeyStore.Data, ""); }
-
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
@@ -36,14 +33,18 @@ export class WebBox extends React.Component<FieldViewProps> {
         }
     }
     render() {
-        let field = this.props.Document.Get(this.props.fieldKey);
-        let path = field === FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" :
-            field instanceof WebField ? field.Data.href : "https://crossorigin.me/" + "https://cs.brown.edu";
-
+        let field = this.props.Document[this.props.fieldKey];
+        let view;
+        if (field instanceof HtmlField) {
+            view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />
+        } else if (field instanceof URLField) {
+            view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />
+        } else {
+            view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />
+        }
         let content =
             <div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
-                {this.html ? <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: this.html }} /> :
-                    <iframe src={path} style={{ position: "absolute", width: "100%", height: "100%" }} />}
+                {view}
             </div>;
 
         return (
-- 
cgit v1.2.3-70-g09d2


From 393d351420b3a0d28f4cd1ea8b674fa5d04bfcde Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Sun, 21 Apr 2019 23:40:48 -0400
Subject: More fixes

---
 .../views/collections/CollectionDockingView.tsx    |  6 ++--
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FieldView.tsx               |  8 ++---
 src/client/views/nodes/ImageBox.tsx                | 15 +++++-----
 src/client/views/nodes/VideoBox.tsx                | 35 ++++++++++++----------
 src/client/views/nodes/WebBox.tsx                  |  4 +--
 src/debug/Test.tsx                                 | 12 ++++----
 src/new_fields/Doc.ts                              |  4 +--
 src/new_fields/Schema.ts                           | 22 +++-----------
 src/new_fields/Types.ts                            | 11 +++----
 src/new_fields/URLField.ts                         |  9 ++++--
 12 files changed, 62 insertions(+), 68 deletions(-)

(limited to 'src/client/views/nodes/WebBox.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 7b204cbdc..86ee7cea9 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -16,7 +16,7 @@ import { ServerUtils } from "../../../server/ServerUtil";
 import { DragManager, DragLinksAsDocuments } from "../../util/DragManager";
 import { Transform } from '../../util/Transform';
 import { Doc, Id, Opt, Field, FieldId } from "../../../new_fields/Doc";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast } from "../../../new_fields/Types";
 import { List } from "../../../new_fields/List";
 import { DocServer } from "../../DocServer";
 
@@ -336,8 +336,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
 
     ScreenToLocalTransform = () => {
         if (this._mainCont.current && this._mainCont.current.children) {
-            let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement);
-            scale = Utils.GetScreenTransform(this._mainCont.current!).scale;
+            let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement);
+            scale = Utils.GetScreenTransform(this._mainCont.current).scale;
             return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling());
         }
         return Transform.Identity();
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 27fd25b5c..c6eea5623 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -18,7 +18,7 @@ const schema = createSchema({
 });
 
 type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const FreeformDocument = makeInterface([schema, positionSchema]);
+const FreeformDocument = makeInterface(schema, positionSchema);
 
 @observer
 export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f1a12e6db..c0afc192e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -56,7 +56,7 @@ export const positionSchema = createSchema({
 });
 
 type Document = makeInterface<[typeof schema]>;
-const Document = makeInterface([schema]);
+const Document = makeInterface(schema);
 
 @observer
 export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 845213366..b11a87d75 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -3,19 +3,19 @@ import { observer } from "mobx-react";
 import { computed } from "mobx";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { ImageBox } from "./ImageBox";
-import { WebBox } from "./WebBox";
 import { VideoBox } from "./VideoBox";
 import { AudioBox } from "./AudioBox";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { Transform } from "../../util/Transform";
-import { returnFalse, emptyFunction, returnOne } from "../../../Utils";
+import { returnFalse, emptyFunction } from "../../../Utils";
 import { CollectionView } from "../collections/CollectionView";
 import { CollectionPDFView } from "../collections/CollectionPDFView";
 import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { IconField } from "../../../fields/IconFIeld";
 import { IconBox } from "./IconBox";
-import { Opt, Doc, Field, FieldValue, FieldWaiting } from "../../../new_fields/Doc";
+import { Opt, Doc, FieldResult } from "../../../new_fields/Doc";
 import { List } from "../../../new_fields/List";
+import { ImageField, VideoField, AudioField } from "../../../new_fields/URLField";
 
 
 //
@@ -47,7 +47,7 @@ export class FieldView extends React.Component<FieldViewProps> {
     }
 
     @computed
-    get field(): FieldValue {
+    get field(): FieldResult {
         const { Document, fieldKey } = this.props;
         return Document[fieldKey];
     }
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 3b72e9f39..c11aee56e 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,16 +14,15 @@ import { createSchema, makeInterface } from '../../../new_fields/Schema';
 import { DocComponent } from '../DocComponent';
 import { positionSchema } from './DocumentView';
 import { FieldValue, Cast } from '../../../new_fields/Types';
-import { URLField } from '../../../new_fields/URLField';
-import { Doc, FieldWaiting } from '../../../new_fields/Doc';
+import { ImageField } from '../../../new_fields/URLField';
 import { List } from '../../../new_fields/List';
 
-const schema = createSchema({
+export const pageSchema = createSchema({
     curPage: "number"
 });
 
-type ImageDocument = makeInterface<[typeof schema, typeof positionSchema]>;
-const ImageDocument = makeInterface([schema, positionSchema]);
+type ImageDocument = makeInterface<[typeof pageSchema, typeof positionSchema]>;
+const ImageDocument = makeInterface(pageSchema, positionSchema);
 
 @observer
 export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageDocument) {
@@ -135,7 +134,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     }
 
     specificContextMenu = (e: React.MouseEvent): void => {
-        let field = Cast(this.Document[this.props.fieldKey], URLField);
+        let field = Cast(this.Document[this.props.fieldKey], ImageField);
         if (field && field !== FieldWaiting) {
             let url = field.url.href;
             ContextMenu.Instance.addItem({
@@ -167,8 +166,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
         let field = this.Document[this.props.fieldKey];
         let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"];
         if (field === FieldWaiting) paths = ["https://image.flaticon.com/icons/svg/66/66163.svg"];
-        else if (field instanceof URLField) paths = [field.url.href];
-        else if (field instanceof List) paths = field.filter(val => val instanceof URLField).map(p => (p as URLField).url.href);
+        else if (field instanceof ImageField) paths = [field.url.href];
+        else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href);
         let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
         return (
             <div className="imageBox-cont" onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9d7c2bc56..7b922b620 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,18 +1,22 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { FieldWaiting, Opt } from '../../../fields/Field';
-import { VideoField } from '../../../fields/VideoField';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
 import Measure from "react-measure";
-import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx";
-import { KeyStore } from "../../../fields/KeyStore";
-import { number } from "prop-types";
+import { action, computed } from "mobx";
+import { DocComponent } from "../DocComponent";
+import { positionSchema } from "./DocumentView";
+import { makeInterface } from "../../../new_fields/Schema";
+import { pageSchema } from "./ImageBox";
+import { Cast, FieldValue } from "../../../new_fields/Types";
+import { VideoField } from "../../../new_fields/URLField";
+
+type VideoDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>;
+const VideoDocument = makeInterface(positionSchema, pageSchema);
 
 @observer
-export class VideoBox extends React.Component<FieldViewProps> {
+export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoDocument) {
 
-    private _reactionDisposer: Opt<IReactionDisposer>;
     private _videoRef = React.createRef<HTMLVideoElement>();
     public static LayoutString() { return FieldView.LayoutString(VideoBox); }
 
@@ -20,7 +24,7 @@ export class VideoBox extends React.Component<FieldViewProps> {
         super(props);
     }
 
-    @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); }
+    @computed private get curPage() { return FieldValue(this.Document.curPage, -1); }
 
 
     _loaded: boolean = false;
@@ -31,12 +35,12 @@ export class VideoBox extends React.Component<FieldViewProps> {
             // bcz: the nativeHeight should really be set when the document is imported.
             //      also, the native dimensions could be different for different pages of the PDF
             //      so this design is flawed.
-            var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0);
-            var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0);
+            var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
+            var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
             var newNativeHeight = nativeWidth * r.entry.height / r.entry.width;
             if (!nativeHeight && newNativeHeight !== nativeHeight && !isNaN(newNativeHeight)) {
-                this.props.Document.SetNumber(KeyStore.Height, newNativeHeight / nativeWidth * this.props.Document.GetNumber(KeyStore.Width, 0));
-                this.props.Document.SetNumber(KeyStore.NativeHeight, newNativeHeight);
+                this.Document.height = newNativeHeight / nativeWidth * FieldValue(this.Document.width, 0);
+                this.Document.nativeHeight = newNativeHeight;
             }
         } else {
             this._loaded = true;
@@ -56,12 +60,11 @@ export class VideoBox extends React.Component<FieldViewProps> {
     }
 
     render() {
-        let field = this.props.Document.GetT(this.props.fieldKey, VideoField);
-        if (!field || field === FieldWaiting) {
+        let field = FieldValue(Cast(this.Document[this.props.fieldKey], VideoField));
+        if (!field) {
             return <div>Loading</div>;
         }
-        let path = field.Data.href;
-        trace();
+        let path = field.url.href;
         return (
             <Measure onResize={this.setScaling}>
                 {({ measureRef }) =>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e3b6e1c05..63778fa50 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { FieldViewProps, FieldView } from './FieldView';
 import { observer } from "mobx-react";
 import { computed } from 'mobx';
 import { HtmlField } from "../../../new_fields/HtmlField";
-import { URLField } from "../../../new_fields/URLField";
+import { WebField } from "../../../new_fields/URLField";
 
 @observer
 export class WebBox extends React.Component<FieldViewProps> {
@@ -37,7 +37,7 @@ export class WebBox extends React.Component<FieldViewProps> {
         let view;
         if (field instanceof HtmlField) {
             view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />
-        } else if (field instanceof URLField) {
+        } else if (field instanceof WebField) {
             view = <iframe src={field.url.href} style={{ position: "absolute", width: "100%", height: "100%" }} />
         } else {
             view = <iframe src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%" }} />
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 81c69ca8e..22a39f31e 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { SerializationHelper } from '../client/util/SerializationHelper';
 import { createSchema, makeInterface, makeStrictInterface } from '../new_fields/Schema';
-import { URLField } from '../new_fields/URLField';
+import { ImageField } from '../new_fields/URLField';
 import { Doc } from '../new_fields/Doc';
 import { ListSpec } from '../new_fields/Types';
 import { List } from '../new_fields/List';
@@ -12,19 +12,19 @@ const schema1 = createSchema({
     hello: "number",
     test: "string",
     fields: "boolean",
-    url: URLField,
+    url: ImageField,
     testDoc: Doc
 });
 
-const TestDoc = makeInterface([schema1]);
+const TestDoc = makeInterface(schema1);
 type TestDoc = makeInterface<[typeof schema1]>;
 
 const schema2 = createSchema({
-    hello: URLField,
+    hello: ImageField,
     test: "boolean",
     fields: { List: "number" } as ListSpec<number>,
     url: "number",
-    testDoc: URLField
+    testDoc: ImageField
 });
 
 const Test2Doc = makeStrictInterface(schema2);
@@ -43,7 +43,7 @@ const assert = (bool: boolean) => {
 
 class Test extends React.Component {
     onClick = () => {
-        const url = new URLField(new URL("http://google.com"));
+        const url = new ImageField(new URL("http://google.com"));
         const doc = new Doc();
         const doc2 = new Doc();
         doc.hello = 5;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 10e8fe7ec..5d18cbb2e 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -33,7 +33,7 @@ export class ObjectField {
 export type Field = number | string | boolean | ObjectField | RefField;
 export type Opt<T> = T | undefined;
 export type FieldWaiting<T extends Field = Field> = Promise<T | undefined>;
-export type FieldValue<T extends Field = Field> = Opt<T> | FieldWaiting<T>;
+export type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<T>;
 
 export const Self = Symbol("Self");
 
@@ -77,7 +77,7 @@ export namespace Doc {
             return Cast(field, ctor);
         });
     }
-    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldValue {
+    export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
         const self = doc[Self];
         return getField(self, key, ignoreProto);
     }
diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts
index 696a5d2a8..3b7078cb0 100644
--- a/src/new_fields/Schema.ts
+++ b/src/new_fields/Schema.ts
@@ -1,13 +1,7 @@
 import { Interface, ToInterface, Cast, FieldCtor, ToConstructor, HasTail, Head, Tail } from "./Types";
 import { Doc, Field, ObjectField } from "./Doc";
-import { URLField } from "./URLField";
 
-type All<T extends Interface[], U extends Doc> = {
-    1: makeInterface<[Head<T>], U> & All<Tail<T>, U>,
-    0: makeInterface<[Head<T>], U>
-}[HasTail<T> extends true ? 1 : 0];
-
-type AllToInterface<T extends any[]> = {
+type AllToInterface<T extends Interface[]> = {
     1: ToInterface<Head<T>> & AllToInterface<Tail<T>>,
     0: ToInterface<Head<T>>
 }[HasTail<T> extends true ? 1 : 0];
@@ -16,7 +10,6 @@ export const emptySchema = createSchema({});
 export const Document = makeInterface(emptySchema);
 export type Document = makeInterface<[typeof emptySchema]>;
 
-const DocSymbol = Symbol("Doc");
 export type makeInterface<T extends Interface[], U extends Doc = Doc> = Partial<AllToInterface<T>> & U;
 // export function makeInterface<T extends Interface[], U extends Doc>(schemas: T): (doc: U) => All<T, U>;
 // export function makeInterface<T extends Interface, U extends Doc>(schema: T): (doc: U) => makeInterface<T, U>; 
@@ -29,26 +22,19 @@ export function makeInterface<T extends Interface[], U extends Doc>(...schemas:
     }
     const proto = new Proxy({}, {
         get(target: any, prop) {
-            if (prop === DocSymbol) {
-                return target[prop];
-            }
-            const field = target[DocSymbol][prop];
+            const field = target.doc[prop];
             if (prop in schema) {
                 return Cast(field, (schema as any)[prop]);
             }
             return field;
         },
         set(target: any, prop, value) {
-            if (prop === DocSymbol) {
-                target[prop] = value;
-            }
-            target[DocSymbol][prop] = value;
+            target.doc[prop] = value;
             return true;
         }
     });
     return function (doc: any) {
-        const obj = Object.create(proto);
-        obj.__doc = doc;
+        const obj = Object.create(proto, { doc: { value: doc, writable: false } });
         return obj;
     };
 }
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index e4d15e276..f4f66fe5c 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,4 +1,4 @@
-import { Field, Opt, FieldWaiting, FieldValue } from "./Doc";
+import { Field, Opt, FieldWaiting, FieldResult } from "./Doc";
 import { List } from "./List";
 
 export type ToType<T> =
@@ -6,7 +6,8 @@ export type ToType<T> =
     T extends "number" ? number :
     T extends "boolean" ? boolean :
     T extends ListSpec<infer U> ? List<U> :
-    T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+    // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+    T extends { new(...args: any[]): infer R } ? R : never;
 
 export type ToConstructor<T> =
     T extends string ? "string" :
@@ -35,9 +36,9 @@ export interface Interface {
 
 export type FieldCtor<T extends Field> = T extends List<infer R> ? ListSpec<R> : ToConstructor<T>;
 
-export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T): FieldValue<ToType<T>>;
+export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T): FieldResult<ToType<T>>;
 export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal: ToType<T>): ToType<T>;
-export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal?: ToType<T>): FieldValue<ToType<T>> | undefined {
+export function Cast<T extends FieldCtor<Field>>(field: Field | FieldWaiting | undefined, ctor: T, defaultVal?: ToType<T>): FieldResult<ToType<T>> | undefined {
     if (field instanceof Promise) {
         return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) : defaultVal;
     }
@@ -66,6 +67,6 @@ export function FieldValue<T extends Field>(field: Opt<T> | Promise<Opt<T>>, def
 export interface PromiseLike<T> {
     then(callback: (field: Opt<T> | PromiseLike<T>) => void): void;
 }
-export function PromiseValue<T extends Field>(field: FieldValue<T>): PromiseLike<Opt<T>> {
+export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
     return field instanceof Promise ? field : { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
 }
\ No newline at end of file
diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts
index e456a7d16..1da245e73 100644
--- a/src/new_fields/URLField.ts
+++ b/src/new_fields/URLField.ts
@@ -13,7 +13,6 @@ function url() {
     );
 }
 
-@Deserializable("url")
 export class URLField extends ObjectField {
     @serializable(url())
     readonly url: URL;
@@ -22,4 +21,10 @@ export class URLField extends ObjectField {
         super();
         this.url = url;
     }
-}
\ No newline at end of file
+}
+
+@Deserializable("audio") export class AudioField extends URLField { }
+@Deserializable("image") export class ImageField extends URLField { }
+@Deserializable("video") export class VideoField extends URLField { }
+@Deserializable("pdf") export class PdfField extends URLField { }
+@Deserializable("web") export class WebField extends URLField { }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

