diff options
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r-- | src/client/documents/Documents.ts | 783 |
1 files changed, 460 insertions, 323 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d1c9feb32..31e7eef2c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -25,7 +25,7 @@ import { OmitKeys } from "../../Utils"; import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; -import { Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast, ToConstructor, InterfaceValue } from "../../new_fields/Types"; import { IconField } from "../../new_fields/IconField"; import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; @@ -34,6 +34,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; +import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; @@ -43,18 +44,42 @@ var path = require('path'); export enum DocTypes { NONE = "none", - IMG = "image", - HIST = "histogram", - ICON = "icon", TEXT = "text", - PDF = "pdf", + HIST = "histogram", + IMG = "image", WEB = "web", COL = "collection", KVP = "kvp", VID = "video", AUDIO = "audio", - LINK = "link", - IMPORT = "import" + PDF = "pdf", + ICON = "icon", + IMPORT = "import", + LINK = "link" +} + +export namespace DocTypeUtils { + + export function values(includeNone: boolean = true): string[] { + let types = Object.values(DocTypes); + return includeNone ? types : types.filter(key => key !== DocTypes.NONE); + } + + export function keys(includeNone: boolean = true): string[] { + let types = Object.keys(DocTypes); + return includeNone ? types : types.filter(key => key !== DocTypes.NONE); + } + + export function toObject<T extends string>(o: Array<T>): { [K in T]: K } { + return o.reduce((res, key) => { + res[key] = key; + return res; + }, Object.create(null)); + } + + const Types = toObject(values()); + + export type All = keyof typeof Types; } export interface DocumentOptions { @@ -84,362 +109,474 @@ export interface DocumentOptions { dbDoc?: Doc; // [key: string]: Opt<Field>; } -const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; -export namespace DocUtils { - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { - if (LinkManager.Instance.doesLinkExist(source, target)) return; - let sv = DocumentManager.Instance.getDocumentView(source); - if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; - if (target === CurrentUserUtils.UserDocument) return; +export namespace Docs { - UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); - linkDoc.type = DocTypes.LINK; - let linkDocProto = Doc.GetProto(linkDoc); + export namespace Prototypes { - linkDocProto.context = targetContext; - linkDocProto.title = title === "" ? source.title + " to " + target.title : title; - linkDocProto.linkDescription = description; - linkDocProto.linkTags = tags; - linkDocProto.type = DocTypes.LINK; + type PrototypeTemplate = { options?: Partial<DocumentOptions>, primary: string, background?: string }; + type TemplateMap = Map<DocTypeUtils.All, PrototypeTemplate>; + type PrototypeMap = Map<DocTypeUtils.All, Doc>; - linkDocProto.anchor1 = source; - linkDocProto.anchor1Page = source.curPage; - linkDocProto.anchor1Groups = new List<Doc>([]); - linkDocProto.anchor2 = target; - linkDocProto.anchor2Page = target.curPage; - linkDocProto.anchor2Groups = new List<Doc>([]); + const LayoutMap: TemplateMap = new Map([ + [DocTypes.TEXT, { + options: { height: 150, backgroundColor: "#f1efeb" }, + primary: FormattedTextBox.LayoutString() + }], + [DocTypes.HIST, { + options: { nativeWidth: 600, curPage: 0 }, + primary: CollectionView.LayoutString("annotations"), + background: HistogramBox.LayoutString() + }], + [DocTypes.IMG, { + options: { height: 300, backgroundColor: "black" }, + primary: CollectionView.LayoutString("annotations"), + background: ImageBox.LayoutString() + }], + [DocTypes.WEB, { + options: { height: 300 }, + primary: WebBox.LayoutString() + }], + [DocTypes.COL, { + options: { panX: 0, panY: 0, scale: 1, width: 500, height: 500 }, + primary: CollectionView.LayoutString() + }], + [DocTypes.KVP, { + options: { height: 150 }, + primary: KeyValueBox.LayoutString() + }], + [DocTypes.VID, { + options: { nativeWidth: 600, curPage: 0 }, + primary: CollectionVideoView.LayoutString("annotations"), + background: VideoBox.LayoutString() + }], + [DocTypes.AUDIO, { + options: { height: 150 }, + primary: AudioBox.LayoutString() + }], + [DocTypes.PDF, { + options: { nativeWidth: 1200, curPage: 1 }, + primary: CollectionPDFView.LayoutString("annotations"), + background: PDFBox.LayoutString() + }], + [DocTypes.ICON, { + options: { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }, + primary: IconBox.LayoutString() + }], + [DocTypes.IMPORT, { + options: { height: 150 }, + primary: DirectoryImportBox.LayoutString() + }] + ]); - LinkManager.Instance.addLink(linkDoc); + const PrototypeMap: PrototypeMap = new Map(); - return linkDoc; - }, "make link"); - } -} + /** + * This function loads or initializes the prototype for each docment type. + * + * This is an asynchronous function because it has to attempt + * to fetch the prototype documents from the server. + * + * Once we have this object that maps the prototype ids to a potentially + * undefined document, we either initialize our private prototype + * variables with the document returned from the server or, if prototypes + * haven't been initialized, the newly initialized prototype document. + */ + export async function initialize(): Promise<void> { + // non-guid string ids for each document prototype + let prototypeIds: string[] = DocTypeUtils.values(false).map(type => type + "Proto"); -export namespace Docs { - let textProto: Doc; - let histoProto: Doc; - let imageProto: Doc; - let webProto: Doc; - let collProto: Doc; - let kvpProto: Doc; - let videoProto: Doc; - let audioProto: Doc; - let pdfProto: Doc; - let iconProto: Doc; - let importProto: Doc; - // let linkProto: Doc; - const textProtoId = "textProto"; - const histoProtoId = "histoProto"; - const pdfProtoId = "pdfProto"; - const imageProtoId = "imageProto"; - const webProtoId = "webProto"; - const collProtoId = "collectionProto"; - const kvpProtoId = "kvpProto"; - const videoProtoId = "videoProto"; - const audioProtoId = "audioProto"; - const iconProtoId = "iconProto"; - const importProtoId = "importProto"; - // const linkProtoId = "linkProto"; - - export function initProtos(): Promise<void> { - return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { - textProto = fields[textProtoId] as Doc || CreateTextPrototype(); - histoProto = fields[histoProtoId] as Doc || CreateHistogramPrototype(); - collProto = fields[collProtoId] as Doc || CreateCollectionPrototype(); - imageProto = fields[imageProtoId] as Doc || CreateImagePrototype(); - webProto = fields[webProtoId] as Doc || CreateWebPrototype(); - kvpProto = fields[kvpProtoId] as Doc || CreateKVPPrototype(); - videoProto = fields[videoProtoId] as Doc || CreateVideoPrototype(); - audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); - pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); - iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); - importProto = fields[importProtoId] as Doc || CreateImportPrototype(); - }); - } + let defaultOptions: DocumentOptions = { + x: 0, + y: 0, + width: 300 + }; - function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Doc { - return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout }); - } - function SetInstanceOptions<U extends Field>(doc: Doc, options: DocumentOptions, value: U) { - const deleg = Doc.MakeDelegate(doc); - deleg.data = value; - return Doc.assign(deleg, options); - } - function SetDelegateOptions(doc: Doc, options: DocumentOptions, id?: string) { - const deleg = Doc.MakeDelegate(doc, id); - return Doc.assign(deleg, options); - } + // fetch the actual prototype documents from the server + let actualProtos = await DocServer.GetRefFields(prototypeIds); + // initialize prototype documents + prototypeIds.map(id => { + let existing = actualProtos[id] as Doc; + if (existing) { + PrototypeMap.set(id, existing); + } else { + let template = LayoutMap.get(id.replace("Proto", "")); + if (template) { + PrototypeMap.set(id, buildPrototype(template, id, defaultOptions)); + } + } + }); + } - function CreateImagePrototype(): Doc { - let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0, type: DocTypes.IMG }); - return imageProto; - } + export function get(type: string) { + return PrototypeMap.get(type)!; + } - function CreateImportPrototype(): Doc { - let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); - return importProto; - } + /** + * This is a convenience method that is used to initialize + * prototype documents for the first time. + * + * @param protoId the id of the prototype, indicating the specific prototype + * to initialize (see the *protoId list at the top of the namespace) + * @param title the prototype document's title, follows *-PROTO + * @param layout the layout key for this prototype and thus the + * layout key that all delegates will inherit + * @param options any value specified in the DocumentOptions object likewise + * becomes the default value for that key for all delegates + */ + function buildPrototype(template: PrototypeTemplate, prototypeId: string, defaultOptions: DocumentOptions): Doc { + let primary = template.primary; + let background = template.background; + let options = { ...defaultOptions, ...(template.options || {}), title: prototypeId.toUpperCase().replace("PROTO", "_PROTO") }; + background && (options = { ...options, backgroundLayout: background, }); + return Doc.assign(new Doc(prototypeId, true), { ...options, layout: primary, baseLayout: primary }); + } - function CreateHistogramPrototype(): Doc { - let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST }); - return histoProto; - } - function CreateIconPrototype(): Doc { - let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(), - { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), type: DocTypes.ICON }); - return iconProto; - } - function CreateTextPrototype(): Doc { - let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), - { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb", type: DocTypes.TEXT }); - return textProto; - } - function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1, type: DocTypes.PDF }); - return pdfProto; - } - function CreateWebPrototype(): Doc { - let webProto = setupPrototypeOptions(webProtoId, "WEB_PROTO", WebBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 300, type: DocTypes.WEB }); - return webProto; - } - function CreateCollectionPrototype(): Doc { - let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), - { panX: 0, panY: 0, scale: 1, width: 500, height: 500, type: DocTypes.COL }); - return collProto; } - function CreateKVPPrototype(): Doc { - let kvpProto = setupPrototypeOptions(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150, type: DocTypes.KVP }); - return kvpProto; - } - function CreateVideoPrototype(): Doc { - let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0, type: DocTypes.VID }); - return videoProto; - } - function CreateAudioPrototype(): Doc { - let audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150, type: DocTypes.AUDIO }); - return audioProto; - } + /** + * Encapsulates the factory used to create new document instances + * delegated from top-level prototypes + */ + export namespace Create { + + const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; + + /** + * This function receives the relevant document prototype and uses + * it to create a new of that base-level prototype, or the + * underlying data document, which it then delegates again + * to create the view document. + * + * It also takes the opportunity to register the user + * that created the document and the time of creation. + * + * @param proto the specific document prototype off of which to model + * this new instance (textProto, imageProto, etc.) + * @param data the Field to store at this new instance's data key + * @param options any initial values to provide for this new instance + * @param delegId if applicable, an existing document id. If undefined, Doc's + * constructor just generates a new GUID. This is currently used + * only when creating a DockDocument from the current user's already existing + * main document. + */ + export function InstanceFromProto(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) { + const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); - export function CreateInstance(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) { - const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); - if (!("author" in protoProps)) { - protoProps.author = CurrentUserUtils.email; + if (!("author" in protoProps)) { + protoProps.author = CurrentUserUtils.email; + } + + if (!("creationDate" in protoProps)) { + protoProps.creationDate = new DateField; + } + + protoProps.isPrototype = true; + + let dataDoc = MakeDataDelegate(proto, protoProps, data); + let viewDoc = Doc.MakeDelegate(dataDoc, delegId); + + return Doc.assign(viewDoc, delegateProps); } - if (!("creationDate" in protoProps)) { - protoProps.creationDate = new DateField; + + /** + * This function receives the relevant top level document prototype + * and models a new instance by delegating from it. + * + * Note that it stores the data it recieves at the delegate's data key, + * and applies any document options to this new delegate / instance. + * @param proto the prototype from which to model this new delegate + * @param options initial values to apply to this new delegate + * @param value the data to store in this new delegate + */ + function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value: D) { + const deleg = Doc.MakeDelegate(proto); + deleg.data = value; + return Doc.assign(deleg, options); } - protoProps.isPrototype = true; - return SetDelegateOptions(SetInstanceOptions(proto, protoProps, data), delegateProps, delegId); - } + export function ImageDocument(url: string, options: DocumentOptions = {}) { + let inst = InstanceFromProto(Prototypes.get(DocTypes.IMG), new ImageField(new URL(url)), { title: path.basename(url), ...options }); + requestImageSize(window.origin + RouteStore.corsProxy + "/" + url) + .then((size: any) => { + let aspect = size.height / size.width; + if (!inst.proto!.nativeWidth) { + inst.proto!.nativeWidth = size.width; + } + inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; + inst.proto!.height = NumCast(inst.proto!.width) * aspect; + }) + .catch((err: any) => console.log(err)); + return inst; + } - export function ImageDocument(url: string, options: DocumentOptions = {}) { - let inst = CreateInstance(imageProto, new ImageField(new URL(url)), { title: path.basename(url), ...options }); - requestImageSize(window.origin + RouteStore.corsProxy + "/" + url) - .then((size: any) => { - let aspect = size.height / size.width; - if (!inst.proto!.nativeWidth) { - inst.proto!.nativeWidth = size.width; - } - inst.proto!.nativeHeight = Number(inst.proto!.nativeWidth!) * aspect; - inst.height = NumCast(inst.width) * aspect; - }) - .catch((err: any) => console.log(err)); - return inst; - // let doc = SetInstanceOptions(GetImagePrototype(), { ...options, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] }, - // [new URL(url), ImageField]); - // doc.SetText(KeyStore.Caption, "my caption..."); - // doc.SetText(KeyStore.BackgroundLayout, EmbeddedCaption()); - // doc.SetText(KeyStore.OverlayLayout, FixedCaption()); - // return doc; - } - export function VideoDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(videoProto, new VideoField(new URL(url)), options); - } - export function AudioDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(audioProto, new AudioField(new URL(url)), options); - } + export function VideoDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.VID), new VideoField(new URL(url)), options); + } - export function DirectoryImportDocument(options: DocumentOptions = {}) { - return CreateInstance(importProto, new List<Doc>(), options); - } + export function AudioDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.AUDIO), new AudioField(new URL(url)), options); + } - export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { - return CreateInstance(histoProto, new HistogramField(histoOp), options); - } - export function TextDocument(options: DocumentOptions = {}) { - return CreateInstance(textProto, "", options); - } - export function IconDocument(icon: string, options: DocumentOptions = {}) { - return CreateInstance(iconProto, new IconField(icon), options); - } + export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.HIST), new HistogramField(histoOp), options); + } - export function PdfDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(pdfProto, new PdfField(new URL(url)), options); - } + export function TextDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.TEXT), "", options); + } + + export function IconDocument(icon: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.ICON), new IconField(icon), options); + } - export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: DocumentOptions = {}) { - let schemaName = options.title ? options.title : "-no schema-"; - let ctlog = await Gateway.Instance.GetSchema(url, schemaName); - if (ctlog && ctlog.schemas) { - let schema = ctlog.schemas[0]; - let schemaDoc = Docs.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); - let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); - if (!schemaDocuments) { - return; + export function PdfDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.PDF), new PdfField(new URL(url)), options); + } + + export async function DBDocument(url: string, options: DocumentOptions = {}, columnOptions: DocumentOptions = {}) { + let schemaName = options.title ? options.title : "-no schema-"; + let ctlog = await Gateway.Instance.GetSchema(url, schemaName); + if (ctlog && ctlog.schemas) { + let schema = ctlog.schemas[0]; + let schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); + let schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); + if (!schemaDocuments) { + return; + } + CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); + const docs = schemaDocuments; + CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { + DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => { + if (field instanceof Doc) { + docs.push(field); + } else { + var atmod = new ColumnAttributeModel(attr); + let histoOp = new HistogramOperation(schema.displayName!, + new AttributeTransformationModel(atmod, AggregateFunction.None), + new AttributeTransformationModel(atmod, AggregateFunction.Count), + new AttributeTransformationModel(atmod, AggregateFunction.Count)); + docs.push(Docs.Create.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! })); + } + })); + }); + return schemaDoc; } - CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); - const docs = schemaDocuments; - CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt<Field>) => { - if (field instanceof Doc) { - docs.push(field); - } else { - var atmod = new ColumnAttributeModel(attr); - let histoOp = new HistogramOperation(schema.displayName!, - new AttributeTransformationModel(atmod, AggregateFunction.None), - new AttributeTransformationModel(atmod, AggregateFunction.Count), - new AttributeTransformationModel(atmod, AggregateFunction.Count)); - docs.push(Docs.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! })); - } - })); - }); - return schemaDoc; + return Docs.Create.TreeDocument([], { width: 50, height: 100, title: schemaName }); } - return Docs.TreeDocument([], { width: 50, height: 100, title: schemaName }); - } - export function WebDocument(url: string, options: DocumentOptions = {}) { - return CreateInstance(webProto, new WebField(new URL(url)), options); - } - export function HtmlDocument(html: string, options: DocumentOptions = {}) { - return CreateInstance(webProto, new HtmlField(html), options); - } - export function KVPDocument(document: Doc, options: DocumentOptions = {}) { - return CreateInstance(kvpProto, document, { title: document.title + ".kvp", ...options }); - } - export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, makePrototype: boolean = true) { - if (!makePrototype) { - return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, new List(documents)); + + export function WebDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.WEB), new WebField(new URL(url)), options); } - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); - } - export function SchemaDocument(schemaColumns: string[], documents: Array<Doc>, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); - } - export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); - } - export function StackingDocument(documents: Array<Doc>, options: DocumentOptions) { - return CreateInstance(collProto, new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); - } - export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) { - return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); - } - export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> { - let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined; - if (type.indexOf("image") !== -1) { - ctor = Docs.ImageDocument; + export function HtmlDocument(html: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.WEB), new HtmlField(html), options); } - if (type.indexOf("video") !== -1) { - ctor = Docs.VideoDocument; + + export function KVPDocument(document: Doc, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.KVP), document, { title: document.title + ".kvp", ...options }); } - if (type.indexOf("audio") !== -1) { - ctor = Docs.AudioDocument; + + export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, makePrototype: boolean = true) { + if (!makePrototype) { + return MakeDataDelegate(Prototypes.get(DocTypes.COL), { ...options, viewType: CollectionViewType.Freeform }, new List(documents)); + } + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); } - if (type.indexOf("pdf") !== -1) { - ctor = Docs.PdfDocument; - options.nativeWidth = 1200; + + export function SchemaDocument(schemaColumns: string[], documents: Array<Doc>, options: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } - if (type.indexOf("excel") !== -1) { - ctor = Docs.DBDocument; - options.dropAction = "copy"; + + export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); + } + + export function StackingDocument(documents: Array<Doc>, options: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); } - if (type.indexOf("html") !== -1) { - if (path.includes(window.location.hostname)) { - let s = path.split('/'); - let id = s[s.length - 1]; - return DocServer.GetRefField(id).then(field => { - if (field instanceof Doc) { - let alias = Doc.MakeAlias(field); - alias.x = options.x || 0; - alias.y = options.y || 0; - alias.width = options.width || 300; - alias.height = options.height || options.width || 300; - return alias; + + export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); + } + + export function DirectoryImportDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocTypes.COL), new List<Doc>(), options); + } + + export type DocConfig = { + doc: Doc, + initialWidth?: number + }; + + export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") { + let layoutConfig = { + content: [ + { + type: type, + content: [ + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth)) + ] } - return undefined; - }); - } - ctor = Docs.WebDocument; - options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + ] + }; + return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); } - return ctor ? ctor(path, options) : undefined; } - export function CaptionDocument(doc: Doc) { - const captionDoc = Doc.MakeAlias(doc); - captionDoc.overlayLayout = FixedCaption(); - captionDoc.width = Cast(doc.width, "number", 0); - captionDoc.height = Cast(doc.height, "number", 0); - return captionDoc; - } + export namespace Get { - // example of custom display string for an image that shows a caption. - function EmbeddedCaption() { - return `<div style="height:100%"> - <div style="position:relative; margin:auto; height:85%; width:85%;" >` - + ImageBox.LayoutString() + - `</div> - <div style="position:relative; height:15%; text-align:center; ">` - + FormattedTextBox.LayoutString("caption") + - `</div> - </div>`; - } - export function FixedCaption(fieldName: string = "caption") { - return `<div style="position:absolute; height:30px; bottom:0; width:100%"> - <div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">` - + FormattedTextBox.LayoutString(fieldName) + - `</div> - </div>`; - } + export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> { + let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined; + if (type.indexOf("image") !== -1) { + ctor = Docs.Create.ImageDocument; + } + if (type.indexOf("video") !== -1) { + ctor = Docs.Create.VideoDocument; + } + if (type.indexOf("audio") !== -1) { + ctor = Docs.Create.AudioDocument; + } + if (type.indexOf("pdf") !== -1) { + ctor = Docs.Create.PdfDocument; + options.nativeWidth = 1200; + } + if (type.indexOf("excel") !== -1) { + ctor = Docs.Create.DBDocument; + options.dropAction = "copy"; + } + if (type.indexOf("html") !== -1) { + if (path.includes(window.location.hostname)) { + let s = path.split('/'); + let id = s[s.length - 1]; + return DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + let alias = Doc.MakeAlias(field); + alias.x = options.x || 0; + alias.y = options.y || 0; + alias.width = options.width || 300; + alias.height = options.height || options.width || 300; + return alias; + } + return undefined; + }); + } + ctor = Docs.Create.WebDocument; + options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + } + return ctor ? ctor(path, options) : undefined; + } - function OuterCaption() { - return (` -<div> - <div style="margin:auto; height:calc(100%); width:100%;"> - {layout} - </div> - <div style="height:(100% + 25px); width:100%; position:absolute"> - <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/> - </div> -</div> - `); } - function InnerCaption() { - return (` - <div> - <div style="margin:auto; height:calc(100% - 25px); width:100%;"> - {layout} - </div> - <div style="height:25px; width:100%; position:absolute"> - <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} renderDepth={renderDepth}/> - </div> - </div> + + export namespace Templating { + + export function CaptionDocument(doc: Doc) { + const captionDoc = Doc.MakeAlias(doc); + captionDoc.overlayLayout = FixedCaption(); + captionDoc.width = Cast(doc.width, "number", 0); + captionDoc.height = Cast(doc.height, "number", 0); + return captionDoc; + } + + /** + * An example of custom display string for an image that shows a caption. + */ + export function EmbeddedCaption() { + return ( + `<div style="height:100%"> + <div style="position:relative; margin:auto; height:85%; width:85%;" >${ImageBox.LayoutString()}</div> + <div style="position:relative; height:15%; text-align:center; ">${FormattedTextBox.LayoutString("caption")}</div> + </div>` + ); + } + + export function FixedCaption(fieldName: string = "caption") { + return ( + `<div style="position:absolute; height:30px; bottom:0; width:100%"> + <div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">${FormattedTextBox.LayoutString(fieldName)}</div> + </div>` + ); + } + + export function OuterCaption() { + return (` + <div> + <div style="margin:auto; height:calc(100%); width:100%;"> + {layout} + </div> + <div style="height:(100% + 25px); width:100%; position:absolute"> + <FormattedTextBox + doc={Document} + DocumentViewForField={DocumentView} + bindings={bindings} + fieldKey={"caption"} + isSelected={isSelected} + select={select} + selectOnLoad={SelectOnLoad} + renderDepth={renderDepth + /> + </div> + </div> + `); + } + + export function InnerCaption() { + return (` + <div> + <div style="margin:auto; height:calc(100% - 25px); width:100%;"> + {layout} + </div> + <div style="height:25px; width:100%; position:absolute"> + <FormattedTextBox + doc={Document} + DocumentViewForField={DocumentView} + bindings={bindings} + fieldKey={"caption"} + isSelected={isSelected} + select={select} + selectOnLoad={SelectOnLoad} + renderDepth={renderDepth + /> + </div> + </div> `); + } + } +} + +export namespace DocUtils { + + export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { + if (LinkManager.Instance.doesLinkExist(source, target)) return; + let sv = DocumentManager.Instance.getDocumentView(source); + if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; + if (target === CurrentUserUtils.UserDocument) return; + + UndoManager.RunInBatch(() => { + let linkDoc = Docs.Create.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); + linkDoc.type = DocTypes.LINK; + let linkDocProto = Doc.GetProto(linkDoc); + + linkDocProto.context = targetContext; + linkDocProto.title = title === "" ? source.title + " to " + target.title : title; + linkDocProto.linkDescription = description; + linkDocProto.linkTags = tags; + linkDocProto.type = DocTypes.LINK; + + linkDocProto.anchor1 = source; + linkDocProto.anchor1Page = source.curPage; + linkDocProto.anchor1Groups = new List<Doc>([]); + linkDocProto.anchor2 = target; + linkDocProto.anchor2Page = target.curPage; + linkDocProto.anchor2Groups = new List<Doc>([]); + + LinkManager.Instance.addLink(linkDoc); + + return linkDoc; + }, "make link"); } } -Scripting.addGlobal("Docs", Docs);
\ No newline at end of file +Scripting.addGlobal("Docs", Docs); |