diff options
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r-- | src/client/documents/Documents.ts | 1494 |
1 files changed, 897 insertions, 597 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 65b3db0e2..ed4c99d70 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,124 +1,158 @@ -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { action, runInAction } from "mobx"; -import { basename } from "path"; -import { DateField } from "../../fields/DateField"; -import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from "../../fields/Doc"; -import { Id } from "../../fields/FieldSymbols"; -import { HtmlField } from "../../fields/HtmlField"; -import { InkField, PointData } from "../../fields/InkField"; -import { List } from "../../fields/List"; -import { ProxyField } from "../../fields/Proxy"; -import { RichTextField } from "../../fields/RichTextField"; -import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; -import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../fields/Types"; -import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; -import { inheritParentAcls, SharingPermissions } from "../../fields/util"; -import { Upload } from "../../server/SharedMediaTypes"; -import { aggregateBounds, OmitKeys, Utils } from "../../Utils"; -import { YoutubeBox } from "../apis/youtube/YoutubeBox"; -import { DocServer } from "../DocServer"; -import { Networking } from "../Network"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { DocumentManager } from "../util/DocumentManager"; -import { dropActionType } from "../util/DragManager"; -import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox"; -import { LinkManager } from "../util/LinkManager"; -import { ScriptingGlobals } from "../util/ScriptingGlobals"; -import { undoBatch, UndoManager } from "../util/UndoManager"; -import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; -import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; -import { ContextMenu } from "../views/ContextMenu"; -import { ContextMenuProps } from "../views/ContextMenuItem"; -import { DFLT_IMAGE_NATIVE_DIM } from "../views/global/globalCssVariables.scss"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke"; -import { AudioBox } from "../views/nodes/AudioBox"; -import { FontIconBox } from "../views/nodes/button/FontIconBox"; -import { ColorBox } from "../views/nodes/ColorBox"; -import { ComparisonBox } from "../views/nodes/ComparisonBox"; -import { DataVizBox } from "../views/nodes/DataVizBox/DataVizBox"; -import { DocFocusOptions } from "../views/nodes/DocumentView"; -import { EquationBox } from "../views/nodes/EquationBox"; -import { FieldViewProps } from "../views/nodes/FieldView"; -import { FilterBox } from "../views/nodes/FilterBox"; -import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; -import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox"; -import { ImageBox } from "../views/nodes/ImageBox"; -import { KeyValueBox } from "../views/nodes/KeyValueBox"; -import { LabelBox } from "../views/nodes/LabelBox"; -import { LinkBox } from "../views/nodes/LinkBox"; -import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup"; -import { MapBox } from "../views/nodes/MapBox/MapBox"; -import { PDFBox } from "../views/nodes/PDFBox"; -import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox"; -import { ScreenshotBox } from "../views/nodes/ScreenshotBox"; -import { ScriptingBox } from "../views/nodes/ScriptingBox"; -import { SliderBox } from "../views/nodes/SliderBox"; -import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; -import { PresBox } from "../views/nodes/trails/PresBox"; -import { PresElementBox } from "../views/nodes/trails/PresElementBox"; -import { VideoBox } from "../views/nodes/VideoBox"; -import { WebBox } from "../views/nodes/WebBox"; -import { SearchBox } from "../views/search/SearchBox"; -import { DocumentType } from "./DocumentTypes"; - -const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { action, runInAction } from 'mobx'; +import { basename } from 'path'; +import { DateField } from '../../fields/DateField'; +import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; +import { HtmlField } from '../../fields/HtmlField'; +import { InkField, PointData } from '../../fields/InkField'; +import { List } from '../../fields/List'; +import { ProxyField } from '../../fields/Proxy'; +import { RichTextField } from '../../fields/RichTextField'; +import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; +import { ComputedField, ScriptField } from '../../fields/ScriptField'; +import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField'; +import { inheritParentAcls, SharingPermissions } from '../../fields/util'; +import { Upload } from '../../server/SharedMediaTypes'; +import { aggregateBounds, OmitKeys, Utils } from '../../Utils'; +import { YoutubeBox } from '../apis/youtube/YoutubeBox'; +import { DocServer } from '../DocServer'; +import { Networking } from '../Network'; +import { DocumentManager } from '../util/DocumentManager'; +import { DragManager, dropActionType } from '../util/DragManager'; +import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox'; +import { LinkManager } from '../util/LinkManager'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { undoBatch, UndoManager } from '../util/UndoManager'; +import { CollectionDockingView } from '../views/collections/CollectionDockingView'; +import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; +import { CollectionView } from '../views/collections/CollectionView'; +import { ContextMenu } from '../views/ContextMenu'; +import { ContextMenuProps } from '../views/ContextMenuItem'; +import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke'; +import { AudioBox } from '../views/nodes/AudioBox'; +import { FontIconBox } from '../views/nodes/button/FontIconBox'; +import { ColorBox } from '../views/nodes/ColorBox'; +import { ComparisonBox } from '../views/nodes/ComparisonBox'; +import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; +import { DocFocusOptions } from '../views/nodes/DocumentView'; +import { EquationBox } from '../views/nodes/EquationBox'; +import { FieldViewProps } from '../views/nodes/FieldView'; +import { FilterBox } from '../views/nodes/FilterBox'; +import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; +import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox'; +import { ImageBox } from '../views/nodes/ImageBox'; +import { KeyValueBox } from '../views/nodes/KeyValueBox'; +import { LabelBox } from '../views/nodes/LabelBox'; +import { LinkBox } from '../views/nodes/LinkBox'; +import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; +import { MapBox } from '../views/nodes/MapBox/MapBox'; +import { PDFBox } from '../views/nodes/PDFBox'; +import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox'; +import { ScreenshotBox } from '../views/nodes/ScreenshotBox'; +import { ScriptingBox } from '../views/nodes/ScriptingBox'; +import { SliderBox } from '../views/nodes/SliderBox'; +import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; +import { PresBox } from '../views/nodes/trails/PresBox'; +import { PresElementBox } from '../views/nodes/trails/PresElementBox'; +import { VideoBox } from '../views/nodes/VideoBox'; +import { WebBox } from '../views/nodes/WebBox'; +import { SearchBox } from '../views/search/SearchBox'; +import { CollectionViewType, DocumentType } from './DocumentTypes'; + +const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); class EmptyBox { public static LayoutString() { - return ""; + return ''; } } export abstract class FInfo { - description: string = ""; + description: string = ''; fieldType?: string; values?: Field[]; // format?: string; // format to display values (e.g, decimal places, $, etc) // parse?: ScriptField; // parse a value from a string - constructor(d: string) { this.description = d; } + constructor(d: string) { + this.description = d; + } +} +class BoolInfo extends FInfo { + fieldType? = 'boolean'; + values?: boolean[] = [true, false]; +} +class NumInfo extends FInfo { + fieldType? = 'number'; + values?: number[] = []; + constructor(d: string, values?: number[]) { + super(d); + this.values = values; + } +} +class StrInfo extends FInfo { + fieldType? = 'string'; + values?: string[] = []; + constructor(d: string, values?: string[]) { + super(d); + this.values = values; + } +} +class DocInfo extends FInfo { + fieldType? = 'Doc'; + values?: Doc[] = []; + constructor(d: string, values?: Doc[]) { + super(d); + this.values = values; + } +} +class DimInfo extends FInfo { + fieldType? = 'DimUnit'; + values? = [DimUnit.Pixel, DimUnit.Ratio]; +} +class PEInfo extends FInfo { + fieldType? = 'pointerEvents'; + values? = ['all', 'none']; +} +class DAInfo extends FInfo { + fieldType? = 'dropActionType'; + values? = ['alias', 'copy', 'move', 'same', 'proto', 'none']; } -class BoolInfo extends FInfo { fieldType?= "boolean"; values?: boolean[] = [true, false]; } -class NumInfo extends FInfo { fieldType?= "number"; values?: number[] = []; constructor(d:string, values?:number[]) { super(d); this.values = values; }} -class StrInfo extends FInfo { fieldType?= "string"; values?: string[] = []; constructor(d:string, values?:string[]) { super(d); this.values = values; }} -class DocInfo extends FInfo { fieldType?= "Doc"; values?: Doc[] = []; constructor(d:string, values?:Doc[]) { super(d); this.values = values; }} -class DimInfo extends FInfo { fieldType?= "DimUnit"; values?= [DimUnit.Pixel, DimUnit.Ratio]; } -class PEInfo extends FInfo { fieldType?= "pointerEvents"; values?= ["all", "none"]; } -class DAInfo extends FInfo { fieldType?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; } type BOOLt = BoolInfo | boolean; -type NUMt = NumInfo | number; -type STRt = StrInfo | string; -type DOCt = DocInfo | Doc; -type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; -type PEVt = PEInfo | "none" | "all"; +type NUMt = NumInfo | number; +type STRt = StrInfo | string; +type DOCt = DocInfo | Doc; +type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; +type PEVt = PEInfo | 'none' | 'all'; type DROPt = DAInfo | dropActionType; export class DocumentOptions { - x?: NUMt = new NumInfo("x coordinate of document in a freeform view"); - y?: NUMt = new NumInfo("y coordinage of document in a freeform view"); - z?: NUMt = new NumInfo("whether document is in overlay (1) or not (0)", [1,0]); - system?: BOOLt = new BoolInfo("is this a system created/owned doc"); - type?: STRt = new StrInfo("type of document", Array.from(Object.keys(DocumentType))); + x?: NUMt = new NumInfo('x coordinate of document in a freeform view'); + y?: NUMt = new NumInfo('y coordinage of document in a freeform view'); + z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', [1, 0]); + system?: BOOLt = new BoolInfo('is this a system created/owned doc'); + type?: STRt = new StrInfo('type of document', Array.from(Object.keys(DocumentType))); title?: string; _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); - allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?"); + allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?'); childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); - targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? "); - userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); - color?: STRt = new StrInfo("foreground color data doc"); - backgroundColor?: STRt = new StrInfo("background color for data doc"); - _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); - _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); - _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); - _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume"); - _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view"); - _panY?: NUMt = new NumInfo("vertical pan location of a freeform view"); - _width?: NUMt = new NumInfo("displayed width of a document"); - _height?: NUMt = new NumInfo("displayed height of document"); - _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)"); - _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)"); + targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? '); + userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)'); + color?: STRt = new StrInfo('foreground color data doc'); + backgroundColor?: STRt = new StrInfo('background color for data doc'); + _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); + _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title'); + _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes'); + _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume'); + _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view'); + _panY?: NUMt = new NumInfo('vertical pan location of a freeform view'); + _width?: NUMt = new NumInfo('displayed width of a document'); + _height?: NUMt = new NumInfo('displayed height of document'); + _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)'); + _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)'); _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); - _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)"); - _fitContentsToBox?: BOOLt = new BoolInfo("whether a freeformview should zoom/scale to create a shrinkwrapped view of its content"); + _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)'); + _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom] _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed @@ -126,11 +160,11 @@ export class DocumentOptions { _showTitle?: string; // field name to display in header (:hover is an optional suffix) _showCaption?: string; // which field to display in the caption area. leave empty to have no caption _scrollTop?: number; // scroll location for pdfs - _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged + _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged _chromeHidden?: boolean; // whether the editing chrome for a document is hidden _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view) _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active) - _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere + _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged. _hideContextMenu?: boolean; // whether the context menu can be shown _viewType?: string; // sub type of a collection @@ -142,7 +176,7 @@ export class DocumentOptions { _xPadding?: number; _yPadding?: number; _itemIndex?: number; // which item index the carousel viewer is showing - _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts + _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document) _minFontSize?: number; // minimum font size for labelBoxes _maxFontSize?: number; // maximum font size for labelBoxes @@ -158,11 +192,11 @@ export class DocumentOptions { _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays) _timecodeToHide?: number; // the time that a document should be hidden _timelineLabel?: boolean; // whether the document exists on a timeline - "_carousel-caption-xMargin"?: NUMt = new NumInfo("x margin of caption inside of a carouself collection"); - "_carousel-caption-yMargin"?: NUMt = new NumInfo("y margin of caption inside of a carouself collection"); - "icon-nativeWidth"?: NUMt = new NumInfo("native width of icon view"); - "icon-nativeHeight"?: NUMt = new NumInfo("native height of icon view"); - "dragFactory-count"?: NUMt = new NumInfo("number of items created from a drag button (used for setting title with incrementing index)"); + '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection'); + '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection'); + 'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view'); + 'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view'); + 'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)'); lat?: number; lng?: number; infoWindowOpen?: boolean; @@ -171,8 +205,8 @@ export class DocumentOptions { fieldValues?: List<any>; // possible field values used by fieldInfos fieldType?: string; // type of afield used by fieldInfos unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf) - "acl-Public"?: string; // public permissions - "_acl-Public"?: string; // public permissions + 'acl-Public'?: string; // public permissions + '_acl-Public'?: string; // public permissions version?: string; // version identifier for a document label?: string; hidden?: boolean; @@ -181,7 +215,7 @@ export class DocumentOptions { mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing" recording?: boolean; // whether WebCam is recording or not autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline. - dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. + dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. toolTip?: string; // tooltip to display on hover contextMenuFilters?: List<ScriptField>; contextMenuScripts?: List<ScriptField>; @@ -190,7 +224,7 @@ export class DocumentOptions { dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) description?: string; // added for links layout?: string | Doc; // default layout string for a document - contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents + contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view) childLayoutString?: string; // template string for collection to use to render its children @@ -230,12 +264,12 @@ export class DocumentOptions { baseProto?: boolean; // is this a base prototoype dontRegisterView?: boolean; lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document. see PresBox. - "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form - "onChildDoubleClick-rawScript"?: string; // onChildDoubleClick script in raw text form - "onChildClick-rawScript"?: string; // on ChildClick script in raw text form - "onClick-rawScript"?: string; // onClick script in raw text form - "onCheckedClick-rawScript"?: string; // onChecked script in raw text form - "onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions + 'onDoubleClick-rawScript'?: string; // onDoubleClick script in raw text form + 'onChildDoubleClick-rawScript'?: string; // onChildDoubleClick script in raw text form + 'onChildClick-rawScript'?: string; // on ChildClick script in raw text form + 'onClick-rawScript'?: string; // onClick script in raw text form + 'onCheckedClick-rawScript'?: string; // onChecked script in raw text form + 'onCheckedClick-params'?: List<string>; // parameter list for onChecked treeview functions columnHeaders?: List<SchemaHeaderField>; // headers for stacking views schemaHeaders?: List<SchemaHeaderField>; // headers for schema view clipWidth?: number; // percent transition from before to after in comparisonBox @@ -270,7 +304,7 @@ export class DocumentOptions { linearViewSubMenu?: boolean; linearViewFloating?: boolean; flexGap?: number; // Linear view flex gap - flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse"; + flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse'; layout_linkView?: Doc; // view template for a link document layout_keyValue?: string; // view tempalte for key value docs @@ -278,8 +312,8 @@ export class DocumentOptions { linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors anchor1?: Doc; anchor2?: Doc; - "anchor1-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) - "anchor2-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) + 'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) + 'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) ignoreClick?: boolean; onClick?: ScriptField; onDoubleClick?: ScriptField; @@ -292,7 +326,7 @@ export class DocumentOptions { clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc; - filterBoolean ?: string; + filterBoolean?: string; useCors?: boolean; icon?: string; target?: Doc; // available for use in scripts as the primary target document @@ -306,9 +340,9 @@ export class DocumentOptions { treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically - treeViewChildDoubleClick?: ScriptField; // + treeViewChildDoubleClick?: ScriptField; // // Action Button - buttonMenu?: boolean; // whether a action button should be displayed + buttonMenu?: boolean; // whether a action button should be displayed buttonMenuDoc?: Doc; explainer?: string; @@ -321,7 +355,7 @@ export class DocumentOptions { treeViewTruncateTitleWidth?: number; treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy - sidebarColor?: string; // background color of text sidebar + sidebarColor?: string; // background color of text sidebar sidebarViewType?: string; // collection type of text sidebar docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents text?: string; @@ -331,192 +365,305 @@ export class DocumentOptions { selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui clipboard?: Doc; searchQuery?: string; // for quersyBox - useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox + useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered linkRelationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor) - linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship + linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship linkColorList?: List<string>; // colors of links corresponding to specific link relationships } export namespace Docs { - export let newAccount: boolean = false; export namespace Prototypes { - type LayoutSource = { LayoutString: (key: string) => string }; type PrototypeTemplate = { layout: { - view: LayoutSource, - dataField: string - }, - data?: any, - options?: Partial<DocumentOptions> + view: LayoutSource; + dataField: string; + }; + data?: any; + options?: Partial<DocumentOptions>; }; type TemplateMap = Map<DocumentType, PrototypeTemplate>; type PrototypeMap = Map<DocumentType, Doc>; - const defaultDataKey = "data"; + const defaultDataKey = 'data'; const TemplateMap: TemplateMap = new Map([ - [DocumentType.RTF, { - layout: { view: FormattedTextBox, dataField: "text" }, - options: { - _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true, - forceReflow: true, links: "@links(self)" - } - }], - [DocumentType.SEARCH, { - layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400, links: "@links(self)" } - }], - [DocumentType.FILTER, { - layout: { view: FilterBox, dataField: defaultDataKey }, - options: { _width: 400, links: "@links(self)" } - }], - [DocumentType.COLOR, { - layout: { view: ColorBox, dataField: defaultDataKey }, - options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" } - }], - [DocumentType.IMG, { - layout: { view: ImageBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.WEB, { - layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" } - }], - [DocumentType.COL, { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" } - }], - [DocumentType.KVP, { - layout: { view: KeyValueBox, dataField: defaultDataKey }, - options: { _fitWidth: true, _height: 150 } - }], - [DocumentType.VID, { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0, links: "@links(self)" }, - }], - [DocumentType.AUDIO, { - layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" } - }], - [DocumentType.REC, { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: "pink", links: "@links(self)" } - }], - [DocumentType.PDF, { - layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" } - }], - [DocumentType.MAP, { - layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" } - }], - [DocumentType.IMPORT, { - layout: { view: DirectoryImportBox, dataField: defaultDataKey }, - options: { _height: 150 } - }], - [DocumentType.LINK, { - layout: { view: LinkBox, dataField: defaultDataKey }, - options: { - childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description", - backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area - links: "@links(self)", - _removeDropProperties: new List(["isLinkButton"]), - } - }], - [DocumentType.LINKDB, { - data: new List<Doc>(), - layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: "alias", title: "Global Link Database" } - }], - [DocumentType.SCRIPTDB, { - data: new List<Doc>(), - layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: "alias", title: "Global Script Database" } - }], - [DocumentType.SCRIPTING, { - layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.YOUTUBE, { - layout: { view: YoutubeBox, dataField: defaultDataKey } - }], - [DocumentType.LABEL, { - layout: { view: LabelBox, dataField: defaultDataKey }, - options: { links: "@links(self)", _singleLine: true } - }], - [DocumentType.EQUATION, { - layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true } - }], - [DocumentType.FUNCPLOT, { - layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { nativeDimModifiable: true, links: "@links(self)" } - }], - [DocumentType.BUTTON, { - layout: { view: LabelBox, dataField: "onClick" }, - options: { links: "@links(self)" } - }], - [DocumentType.SLIDER, { - layout: { view: SliderBox, dataField: defaultDataKey, }, - options: { links: "@links(self)", treeViewGrowsHorizontally: true } - }], - [DocumentType.PRES, { - layout: { view: PresBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.FONTICON, { - layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" }, - }], - [DocumentType.WEBCAM, { - layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.PRESELEMENT, { - layout: { view: PresElementBox, dataField: defaultDataKey } - }], - [DocumentType.MARKER, { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { links: "@links(self)", hideLinkButton: true, pointerEvents: "none" } - }], - [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method - layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.SCREENSHOT, { - layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.COMPARISON, { - layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } - }], - [DocumentType.GROUPDB, { - data: new List<Doc>(), - layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { childDropAction: "alias", title: "Global Group Database" } - }], - [DocumentType.GROUP, { - layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } - }], - [DocumentType.DATAVIZ, { - layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" } - }] + [ + DocumentType.RTF, + { + layout: { view: FormattedTextBox, dataField: 'text' }, + options: { + _height: 35, + _xMargin: 10, + _yMargin: 10, + nativeDimModifiable: true, + treeViewGrowsHorizontally: true, + forceReflow: true, + links: '@links(self)', + }, + }, + ], + [ + DocumentType.SEARCH, + { + layout: { view: SearchBox, dataField: defaultDataKey }, + options: { _width: 400, links: '@links(self)' }, + }, + ], + [ + DocumentType.FILTER, + { + layout: { view: FilterBox, dataField: defaultDataKey }, + options: { _width: 400, links: '@links(self)' }, + }, + ], + [ + DocumentType.COLOR, + { + layout: { view: ColorBox, dataField: defaultDataKey }, + options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' }, + }, + ], + [ + DocumentType.IMG, + { + layout: { view: ImageBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.WEB, + { + layout: { view: WebBox, dataField: defaultDataKey }, + options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + }, + ], + [ + DocumentType.COL, + { + layout: { view: CollectionView, dataField: defaultDataKey }, + options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' }, + }, + ], + [ + DocumentType.KVP, + { + layout: { view: KeyValueBox, dataField: defaultDataKey }, + options: { _fitWidth: true, _height: 150 }, + }, + ], + [ + DocumentType.VID, + { + layout: { view: VideoBox, dataField: defaultDataKey }, + options: { _currentTimecode: 0, links: '@links(self)' }, + }, + ], + [ + DocumentType.AUDIO, + { + layout: { view: AudioBox, dataField: defaultDataKey }, + options: { _height: 100, backgroundColor: 'lightGray', forceReflow: true, nativeDimModifiable: true, links: '@links(self)' }, + }, + ], + [ + DocumentType.REC, + { + layout: { view: VideoBox, dataField: defaultDataKey }, + options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' }, + }, + ], + [ + DocumentType.PDF, + { + layout: { view: PDFBox, dataField: defaultDataKey }, + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + }, + ], + [ + DocumentType.MAP, + { + layout: { view: MapBox, dataField: defaultDataKey }, + options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' }, + }, + ], + [ + DocumentType.IMPORT, + { + layout: { view: DirectoryImportBox, dataField: defaultDataKey }, + options: { _height: 150 }, + }, + ], + [ + DocumentType.LINK, + { + layout: { view: LinkBox, dataField: defaultDataKey }, + options: { + childDontRegisterViews: true, + _isLinkButton: true, + _height: 150, + description: '', + showCaption: 'description', + backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area + links: '@links(self)', + _removeDropProperties: new List(['isLinkButton']), + }, + }, + ], + [ + DocumentType.LINKDB, + { + data: new List<Doc>(), + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { childDropAction: 'alias', title: 'Global Link Database' }, + }, + ], + [ + DocumentType.SCRIPTDB, + { + data: new List<Doc>(), + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { childDropAction: 'alias', title: 'Global Script Database' }, + }, + ], + [ + DocumentType.SCRIPTING, + { + layout: { view: ScriptingBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.YOUTUBE, + { + layout: { view: YoutubeBox, dataField: defaultDataKey }, + }, + ], + [ + DocumentType.LABEL, + { + layout: { view: LabelBox, dataField: defaultDataKey }, + options: { links: '@links(self)', _singleLine: true }, + }, + ], + [ + DocumentType.EQUATION, + { + layout: { view: EquationBox, dataField: defaultDataKey }, + options: { links: '@links(self)', nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true }, + }, + ], + [ + DocumentType.FUNCPLOT, + { + layout: { view: FunctionPlotBox, dataField: defaultDataKey }, + options: { nativeDimModifiable: true, links: '@links(self)' }, + }, + ], + [ + DocumentType.BUTTON, + { + layout: { view: LabelBox, dataField: 'onClick' }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.SLIDER, + { + layout: { view: SliderBox, dataField: defaultDataKey }, + options: { links: '@links(self)', treeViewGrowsHorizontally: true }, + }, + ], + [ + DocumentType.PRES, + { + layout: { view: PresBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.FONTICON, + { + layout: { view: FontIconBox, dataField: defaultDataKey }, + options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' }, + }, + ], + [ + DocumentType.WEBCAM, + { + layout: { view: RecordingBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.PRESELEMENT, + { + layout: { view: PresElementBox, dataField: defaultDataKey }, + }, + ], + [ + DocumentType.MARKER, + { + layout: { view: CollectionView, dataField: defaultDataKey }, + options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' }, + }, + ], + [ + DocumentType.INK, + { + // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method + layout: { view: InkingStroke, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.SCREENSHOT, + { + layout: { view: ScreenshotBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.COMPARISON, + { + layout: { view: ComparisonBox, dataField: defaultDataKey }, + options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' }, + }, + ], + [ + DocumentType.GROUPDB, + { + data: new List<Doc>(), + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { childDropAction: 'alias', title: 'Global Group Database' }, + }, + ], + [ + DocumentType.GROUP, + { + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { links: '@links(self)' }, + }, + ], + [ + DocumentType.DATAVIZ, + { + layout: { view: DataVizBox, dataField: defaultDataKey }, + options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' }, + }, + ], ]); - const suffix = "Proto"; + const suffix = 'Proto'; /** * 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 @@ -526,13 +673,15 @@ export namespace Docs { ProxyField.initPlugin(); ComputedField.initPlugin(); // non-guid string ids for each document prototype - const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); + const prototypeIds = Object.values(DocumentType) + .filter(type => type !== DocumentType.NONE) + .map(type => type + suffix); // fetch the actual prototype documents from the server const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds); // update this object to include any default values: DocumentOptions for all prototypes prototypeIds.map(id => { const existing = actualProtos[id] as Doc; - const type = id.replace(suffix, "") as DocumentType; + const type = id.replace(suffix, '') as DocumentType; // get or create prototype of the specified type... const target = existing || buildPrototype(type, id); // ...and set it if not undefined (can be undefined only if TemplateMap does not contain @@ -545,30 +694,38 @@ export namespace Docs { * Retrieves the prototype for the given document type, or * undefined if that type's proto doesn't have a configuration * in the template map. - * @param type + * @param type */ const PrototypeMap: PrototypeMap = new Map(); - export function get(type: DocumentType): Doc { return PrototypeMap.get(type)!; } + export function get(type: DocumentType): Doc { + return PrototypeMap.get(type)!; + } /** * A collection of all links in the database. Ideally, this would be a search, but for now all links are cached here. */ - export function MainLinkDocument() { return Prototypes.get(DocumentType.LINKDB); } + export function MainLinkDocument() { + return Prototypes.get(DocumentType.LINKDB); + } /** * A collection of all scripts in the database */ - export function MainScriptDocument() { return Prototypes.get(DocumentType.SCRIPTDB); } + export function MainScriptDocument() { + return Prototypes.get(DocumentType.SCRIPTDB); + } /** * A collection of all user acl groups in the database */ - export function MainGroupDocument() { return Prototypes.get(DocumentType.GROUPDB); } + export function MainGroupDocument() { + return Prototypes.get(DocumentType.GROUPDB); + } /** * 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 @@ -590,11 +747,21 @@ export namespace Docs { // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype const options: DocumentOptions = { - system: true, _layoutKey: "layout", title, type, baseProto: true, x: 0, y: 0, _width: 300, ...(template.options || {}), - layout: layout.view?.LayoutString(layout.dataField), data: template.data, layout_keyValue: KeyValueBox.LayoutString("") + system: true, + _layoutKey: 'layout', + title, + type, + baseProto: true, + x: 0, + y: 0, + _width: 300, + ...(template.options || {}), + layout: layout.view?.LayoutString(layout.dataField), + data: template.data, + layout_keyValue: KeyValueBox.LayoutString(''), }; Object.entries(options).map(pair => { - if (typeof pair[1] === "string" && pair[1].startsWith("@")) { + if (typeof pair[1] === 'string' && pair[1].startsWith('@')) { (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1)); } }); @@ -607,16 +774,15 @@ export namespace Docs { * delegated from top-level prototypes */ export namespace Create { - /** * 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 + * 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 @@ -626,45 +792,48 @@ export namespace Docs { * only when creating a DockDocument from the current user's already existing * main document. */ - function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) { - const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_" - const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); + function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string) { + const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_" + const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); - dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + dataProps['acl-Override'] = 'None'; + dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps.system = viewProps.system; dataProps.isPrototype = true; dataProps.author = Doc.CurrentUserEmail; - dataProps.creationDate = new DateField; - dataProps[`${fieldKey}-lastModified`] = new DateField; + dataProps.creationDate = new DateField(); + dataProps[`${fieldKey}-lastModified`] = new DateField(); dataProps[fieldKey] = data; // so that the list of annotations is already initialised, prevents issues in addonly. // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do. - dataProps[fieldKey + "-annotations"] = new List<Doc>(); - dataProps[fieldKey + "-sidebar"] = new List<Doc>(); + dataProps[fieldKey + '-annotations'] = new List<Doc>(); + dataProps[fieldKey + '-sidebar'] = new List<Doc>(); const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); const viewFirstProps: { [id: string]: any } = {}; - viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; - viewFirstProps["acl-Override"] = "None"; + viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + viewFirstProps['acl-Override'] = 'None'; viewFirstProps.author = Doc.CurrentUserEmail; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); Doc.assign(viewDoc, viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); - !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && - !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc); + !Doc.IsSystem(dataDoc) && + ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && + !dataDoc.isFolder && + !dataProps.annotationOn && + Doc.AddDocToList(Doc.MyFileOrphans, undefined, dataDoc); updateCachedAcls(dataDoc); updateCachedAcls(viewDoc); return viewDoc; } - export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) { - const imgField = url instanceof ImageField ? url : new ImageField(url); + export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) { + const imgField = url instanceof ImageField ? url : new ImageField(url); return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }); } @@ -672,9 +841,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options); } - export function ScriptingDocument(script: Opt<ScriptField>|null, options: DocumentOptions = {}, fieldKey?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined, - { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); + export function ScriptingDocument(script: Opt<ScriptField> | null, options: DocumentOptions = {}, fieldKey?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); } export function VideoDocument(url: string, options: DocumentOptions = {}) { @@ -686,24 +854,23 @@ export namespace Docs { } export function WebCamDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options); + return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), '', options); } export function ScreenshotDocument(options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options); + return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), '', options); } - export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) { - return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", options); + export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) { + return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options); } export function AudioDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), - { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); + return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); } export function RecordingDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options); + return InstanceFromProto(Prototypes.get(DocumentType.REC), '', options); } export function SearchDocument(options: DocumentOptions = {}) { @@ -711,34 +878,46 @@ export namespace Docs { } export function ColorDocument(options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.COLOR), "", options); + return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } - export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = "text") { + export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') { return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } - export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = "text") { + export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = 'text') { const rtf = { doc: { - type: "doc", content: [{ - type: "paragraph", - content: [{ - type: "text", - text - }] - }] + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text, + }, + ], + }, + ], }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] + selection: { type: 'text', anchor: 1, head: 1 }, + storedMarks: [], }; const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined; return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } - export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { - const linkDoc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { - anchor1: source.doc, anchor2: target.doc, ...options - }, id); + export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) { + const linkDoc = InstanceFromProto( + Prototypes.get(DocumentType.LINK), + undefined, + { + anchor1: source.doc, + anchor2: target.doc, + ...options, + }, + id + ); LinkManager.Instance.addLink(linkDoc); @@ -749,9 +928,9 @@ export namespace Docs { const I = new Doc(); I[Initializing] = true; I.type = DocumentType.INK; - I.layout = InkingStroke.LayoutString("data"); + I.layout = InkingStroke.LayoutString('data'); I.color = color; - I.hideDecorationTitle = true; // don't show title when selected + I.hideDecorationTitle = true; // don't show title when selected // I.hideOpenButton = true; // don't show open full screen button when selected I.fillColor = fillColor; I.strokeWidth = strokeWidth; @@ -760,20 +939,20 @@ export namespace Docs { I.strokeEndMarker = arrowEnd; I.strokeDash = dash; I.tool = tool; - I["text-align"] = "center"; - I.title = "ink"; + I['text-align'] = 'center'; + I.title = 'ink'; I.x = options.x as number; I.y = options.y as number; I._width = options._width as number; I._height = options._height as number; - I._fontFamily = "cursive"; + I._fontFamily = 'cursive'; I.author = Doc.CurrentUserEmail; I.rotation = 0; I.data = new InkField(points); - I.creationDate = new DateField; - I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; - I["acl-Override"] = "None"; - I.links = ComputedField.MakeFunction("links(self)"); + I.creationDate = new DateField(); + I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + I['acl-Override'] = 'None'; + I.links = ComputedField.MakeFunction('links(self)'); I[Initializing] = false; return I; } @@ -783,7 +962,7 @@ export namespace Docs { const height = options._height || undefined; const nwid = options._nativeWidth || undefined; const nhght = options._nativeHeight || undefined; - if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width); + if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width); return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options); } @@ -792,8 +971,8 @@ export namespace Docs { const height = options._height || undefined; const nwid = options._nativeWidth || undefined; const nhght = options._nativeHeight || undefined; - if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width); - return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : "http://www.bing.com/"), options); + if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : 'http://www.bing.com/'), options); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { @@ -809,12 +988,12 @@ export namespace Docs { } export function KVPDocument(document: Doc, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + ".kvp", ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options }); } export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) { const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id); - documents.map(d => d.context = inst); + documents.map(d => (d.context = inst)); return inst; } @@ -831,7 +1010,7 @@ export namespace Docs { } export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: "visible", _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); } export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) { @@ -886,7 +1065,7 @@ export namespace Docs { } export function ButtonDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" }); + return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), 'onClick-rawScript': '-script-' }); } export function SliderDocument(options?: DocumentOptions) { @@ -905,11 +1084,11 @@ export namespace Docs { } export function DataVizDocument(url: string, options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: "Data Viz", ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }); } export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { @@ -917,23 +1096,26 @@ export namespace Docs { } export type DocConfig = { - doc: Doc, - initialWidth?: number, - path?: Doc[] + doc: Doc; + initialWidth?: number; + path?: Doc[]; }; - export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") { + export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = 'row') { const layoutConfig = { content: [ { type: type, - content: [ - ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth)) - ] - } - ] + content: [...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))], + }, + ], }; - return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); + return DockDocument( + configs.map(c => c.doc), + JSON.stringify(layoutConfig), + options, + id + ); } export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) { @@ -944,9 +1126,9 @@ export namespace Docs { export namespace DocUtils { export function Excluded(d: Doc, docFilters: string[]) { - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields + const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields docFilters.forEach(filter => { - const fields = filter.split(":"); + const fields = filter.split(':'); const key = fields[0]; const value = fields[1]; const modifiers = fields[2]; @@ -959,7 +1141,7 @@ export namespace DocUtils { if (d.z) return false; for (const facetKey of Object.keys(filterFacets)) { const facet = filterFacets[facetKey]; - const xs = Object.keys(facet).filter(value => facet[value] === "x"); + const xs = Object.keys(facet).filter(value => facet[value] === 'x'); const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value)); if (failsNotEqualFacets) { return true; @@ -968,21 +1150,21 @@ export namespace DocUtils { return false; } /** - * @param docs - * @param docFilters - * @param docRangeFilters - * @param viewSpecScript - * Given a list of docs and docFilters, @returns the list of Docs that match those filters + * @param docs + * @param docFilters + * @param docRangeFilters + * @param viewSpecScript + * Given a list of docs and docFilters, @returns the list of Docs that match those filters */ export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField, parentCollection?: Doc) { const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; if (!docFilters?.length && !docRangeFilters?.length) { - return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one + return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one } - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields + const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields docFilters.forEach(filter => { - const fields = filter.split(":"); + const fields = filter.split(':'); const key = fields[0]; const value = fields[1]; const modifiers = fields[2]; @@ -992,63 +1174,67 @@ export namespace DocUtils { filterFacets[key][value] = modifiers; }); - const filteredDocs = docFilters.length ? childDocs.filter(d => { - if (d.z) return true; - // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter - if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { - return false; - } - - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== "cookies")) { - const facet = filterFacets[facetKey]; - - // facets that match some value in the field of the document (e.g. some text field) - const matches = Object.keys(facet).filter(value => value !== "cookies" && facet[value] === "match"); - - // facets that have a check next to them - const checks = Object.keys(facet).filter(value => facet[value] === "check"); - - // metadata facets that exist - const exists = Object.keys(facet).filter(value => facet[value] === "exists"); - - // metadata facets that exist - const unsets = Object.keys(facet).filter(value => facet[value] === "unset"); - - // facets that have an x next to them - const xs = Object.keys(facet).filter(value => facet[value] === "x"); - - if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; - const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); - const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined); - const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined); - const satisfiesMatchFacets = !matches.length ? true : matches.some(value => { - if (facetKey.startsWith("*")) { // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc - const allKeys = Array.from(Object.keys(d)); - allKeys.push(...Object.keys(Doc.GetProto(d))); - const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); - return keys.some(key => Field.toString(d[key] as Field).includes(value)); - } - return Field.toString(d[facetKey] as Field).includes(value); - }); - // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria - if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") { - if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; - } - // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria - else { - if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; - } - - } - return (parentCollection?.currentFilter as Doc)?.filterBoolean === "OR" ? false : true; - }) : childDocs; + const filteredDocs = docFilters.length + ? childDocs.filter(d => { + if (d.z) return true; + // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter + if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { + return false; + } + + for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) { + const facet = filterFacets[facetKey]; + + // facets that match some value in the field of the document (e.g. some text field) + const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); + + // facets that have a check next to them + const checks = Object.keys(facet).filter(value => facet[value] === 'check'); + + // metadata facets that exist + const exists = Object.keys(facet).filter(value => facet[value] === 'exists'); + + // metadata facets that exist + const unsets = Object.keys(facet).filter(value => facet[value] === 'unset'); + + // facets that have an x next to them + const xs = Object.keys(facet).filter(value => facet[value] === 'x'); + + if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; + const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value)); + const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value)); + const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined); + const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined); + const satisfiesMatchFacets = !matches.length + ? true + : matches.some(value => { + if (facetKey.startsWith('*')) { + // fields starting with a '*' are used to match families of related fields. ie, *lastModified will match text-lastModified, data-lastModified, etc + const allKeys = Array.from(Object.keys(d)); + allKeys.push(...Object.keys(Doc.GetProto(d))); + const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); + return keys.some(key => Field.toString(d[key] as Field).includes(value)); + } + return Field.toString(d[facetKey] as Field).includes(value); + }); + // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria + if ((parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR') { + if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + } + // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria + else { + if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + } + } + return (parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR' ? false : true; + }) + : childDocs; const rangeFilteredDocs = filteredDocs.filter(d => { for (let i = 0; i < docRangeFilters.length; i += 3) { const key = docRangeFilters[i]; const min = Number(docRangeFilters[i + 1]); const max = Number(docRangeFilters[i + 2]); - const val = typeof d[key] === "string" ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], "number", null); + const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); if (val === undefined) { //console.log("Should 'undefined' pass range filter or not?") } else if (val < min || val > max) return false; @@ -1059,7 +1245,7 @@ export namespace DocUtils { } export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) { - targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, ""); + targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, ''); DocServer.GetRefField(targetID).then(doc => { if (promoteDoc !== doc) { let copy = doc as Doc; @@ -1073,16 +1259,17 @@ export namespace DocUtils { remDoc && remDoc(promoteDoc); if (!doc) { DocListCastAsync(promoteDoc.links).then(links => { - links && links.map(async link => { - if (link) { - const a1 = await Cast(link.anchor1, Doc); - if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy; - const a2 = await Cast(link.anchor2, Doc); - if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy; - LinkManager.Instance.deleteLink(link); - LinkManager.Instance.addLink(link); - } - }); + links && + links.map(async link => { + if (link) { + const a1 = await Cast(link.anchor1, Doc); + if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy; + const a2 = await Cast(link.anchor2, Doc); + if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy; + LinkManager.Instance.deleteLink(link); + LinkManager.Instance.addLink(link); + } + }); }); } } @@ -1093,19 +1280,19 @@ export namespace DocUtils { options?.afterFocus?.(false); } - export let ActiveRecordings: { props: FieldViewProps, getAnchor: () => Doc }[] = []; + export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = []; export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) { - broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1); + broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { - const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline"); - link && (link.followLinkLocation = "add:right"); + const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline'); + link && (link.followLinkLocation = 'add:right'); return link; }); } - export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { - if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link"; + export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { + if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; const sv = DocumentManager.Instance.getDocumentView(source.doc); if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return; if (target.doc === Doc.UserDoc()) return undefined; @@ -1114,7 +1301,7 @@ export namespace DocUtils { if (showPopup) { LinkManager.currentLink = linkDoc; - TaskCompletionBox.textDisplayed = "Link Created"; + TaskCompletionBox.textDisplayed = 'Link Created'; TaskCompletionBox.popupX = showPopup[0]; TaskCompletionBox.popupY = showPopup[1] - 33; TaskCompletionBox.taskCompleted = true; @@ -1133,25 +1320,75 @@ export namespace DocUtils { TaskCompletionBox.popupY -= 40; } - setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); + setTimeout( + action(() => (TaskCompletionBox.taskCompleted = false)), + 2500 + ); } return linkDoc; }); - return makeLink(Docs.Create.LinkDocument(source, target, { - title: ComputedField.MakeFunction("generateLinkTitle(self)") as any, - "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined, - "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined, - "acl-Public": SharingPermissions.Augment, - "_acl-Public": SharingPermissions.Augment, - linkDisplay: true, - _hidden: true, - _linkAutoMove: true, - linkRelationship, - _showCaption: "description", - _showTitle: "linkRelationship", - description - }, id), showPopup); + return makeLink( + Docs.Create.LinkDocument( + source, + target, + { + title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, + 'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined, + 'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined, + 'acl-Public': SharingPermissions.Augment, + '_acl-Public': SharingPermissions.Augment, + linkDisplay: true, + _hidden: true, + _linkAutoMove: true, + linkRelationship, + _showCaption: 'description', + _showTitle: 'linkRelationship', + description, + }, + id + ), + showPopup + ); + } + + export function AssignScripts(doc: Doc, scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { + scripts && + Object.keys(scripts).map(key => { + if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { + doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value: 'any', scriptContext: 'any', documentView: Doc.name }, { _readOnly_: true }); + } + }); + funcs && + Object.keys(funcs).map(key => { + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) { + doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }); + } + }); + return doc; + } + export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) { + if (doc) { + const compareValues = (val1: any, val2: any) => { + if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { + return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); + } + return val1 === val2; + }; + Object.entries(reqdOpts).forEach(pair => { + const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc); + if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) { + targetDoc[pair[0]] = pair[1]; + } + }); + items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item)); + items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item)); + } + return doc; + } + export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { + return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); } export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { @@ -1160,24 +1397,24 @@ export namespace DocUtils { const field = target[fieldKey]; const resolved = options || {}; if (field instanceof ImageField) { - created = Docs.Create.ImageDocument((field).url.href, resolved); + created = Docs.Create.ImageDocument(field.url.href, resolved); layout = ImageBox.LayoutString; } else if (field instanceof Doc) { created = field; } else if (field instanceof VideoField) { - created = Docs.Create.VideoDocument((field).url.href, resolved); + created = Docs.Create.VideoDocument(field.url.href, resolved); layout = VideoBox.LayoutString; } else if (field instanceof PdfField) { - created = Docs.Create.PdfDocument((field).url.href, resolved); + created = Docs.Create.PdfDocument(field.url.href, resolved); layout = PDFBox.LayoutString; } else if (field instanceof AudioField) { - created = Docs.Create.AudioDocument((field).url.href, resolved); + created = Docs.Create.AudioDocument(field.url.href, resolved); layout = AudioBox.LayoutString; } else if (field instanceof RecordingField) { - created = Docs.Create.RecordingDocument((field).url.href, resolved); + created = Docs.Create.RecordingDocument(field.url.href, resolved); layout = RecordingBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); @@ -1185,9 +1422,8 @@ export namespace DocUtils { } else if (field instanceof MapField) { created = Docs.Create.MapDocument(DocListCast(field), resolved); layout = MapBox.LayoutString; - } - else { - created = Docs.Create.TextDocument("", { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved }); + } else { + created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved }); layout = FormattedTextBox.LayoutString; } if (created) { @@ -1199,28 +1435,28 @@ export namespace DocUtils { } 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) { + let ctor: ((path: string, options: DocumentOptions) => Doc | Promise<Doc | undefined>) | undefined = undefined; + if (type.indexOf('image') !== -1) { ctor = Docs.Create.ImageDocument; if (!options._width) options._width = 300; } - if (type.indexOf("video") !== -1) { + if (type.indexOf('video') !== -1) { ctor = Docs.Create.VideoDocument; if (!options._width) options._width = 600; - if (!options._height) options._height = (options._width as number) * 2 / 3; + if (!options._height) options._height = ((options._width as number) * 2) / 3; } - if (type.indexOf("audio") !== -1) { + if (type.indexOf('audio') !== -1) { ctor = Docs.Create.AudioDocument; } - if (type.indexOf("pdf") !== -1) { + if (type.indexOf('pdf') !== -1) { ctor = Docs.Create.PdfDocument; if (!options._width) options._width = 400; - if (!options._height) options._height = (options._width as number) * 1200 / 927; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; } - if (type.indexOf("csv") !== -1) { + if (type.indexOf('csv') !== -1) { ctor = Docs.Create.DataVizDocument; if (!options._width) options._width = 400; - if (!options._height) options._height = (options._width as number) * 1200 / 927; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; } //TODO:al+glr // if (type.indexOf("map") !== -1) { @@ -1228,15 +1464,15 @@ export namespace DocUtils { // if (!options._width) options._width = 800; // if (!options._height) options._height = (options._width as number) * 3 / 4; // } - if (type.indexOf("html") !== -1) { + if (type.indexOf('html') !== -1) { if (path.includes(window.location.hostname)) { const s = path.split('/'); const id = s[s.length - 1]; return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { const alias = Doc.MakeAlias(field); - alias.x = options.x as number || 0; - alias.y = options.y as number || 0; + alias.x = (options.x as number) || 0; + alias.y = (options.y as number) || 0; alias._width = (options._width as number) || 300; alias._height = (options._height as number) || (options._width as number) || 300; return alias; @@ -1245,55 +1481,63 @@ export namespace DocUtils { }); } ctor = Docs.Create.WebDocument; - options = { ...options, _width: 400, _height: 512, title: path, }; + options = { ...options, _width: 400, _height: 512, title: path }; } - + return ctor ? ctor(path, options) : undefined; } export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void { - !simpleMenu && ContextMenu.Instance.addItem({ - description: "Quick Notes", - subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({ - description: ":" + StrCast(note.title), - event: undoBatch((args: { x: number, y: number }) => { - const textDoc = Docs.Create.TextDocument("", { - _width: 200, x, y, _autoHeight: note._autoHeight !== false, - title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1) - }); - textDoc.layoutKey = "layout_" + note.title; - textDoc[textDoc.layoutKey] = note; - docTextAdder(textDoc); + !simpleMenu && + ContextMenu.Instance.addItem({ + description: 'Quick Notes', + subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({ + description: ':' + StrCast(note.title), + event: undoBatch((args: { x: number; y: number }) => { + const textDoc = Docs.Create.TextDocument('', { + _width: 200, + x, + y, + _autoHeight: note._autoHeight !== false, + title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1), + }); + textDoc.layoutKey = 'layout_' + note.title; + textDoc[textDoc.layoutKey] = note; + docTextAdder(textDoc); + }), + icon: StrCast(note.icon) as IconProp, + })) as ContextMenuProps[], + icon: 'sticky-note', + }); + const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation) + .map((dragDoc, i) => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoBatch((args: { x: number; y: number }) => { + const newDoc = DocUtils.copyDragFactory(dragDoc); + if (newDoc) { + newDoc.author = Doc.CurrentUserEmail; + newDoc.x = x; + newDoc.y = y; + EquationBox.SelectOnLoad = newDoc[Id]; + if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; + docAdder?.(newDoc); + } }), - icon: StrCast(note.icon) as IconProp - })) as ContextMenuProps[], - icon: "sticky-note" - }); - const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ - description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), - event: undoBatch((args: { x: number, y: number }) => { - const newDoc = DocUtils.copyDragFactory(dragDoc); - if (newDoc) { - newDoc.author = Doc.CurrentUserEmail; - newDoc.x = x; - newDoc.y = y; - EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; - docAdder?.(newDoc); - } - }), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; ContextMenu.Instance.addItem({ - description: "Create document", + description: 'Create document', subitems: documentList, - icon: "file" + icon: 'file', }); - }// applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { - const batch = UndoManager.StartBatch("makeCustomViewClicked"); + } // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) + export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { + const batch = UndoManager.StartBatch('makeCustomViewClicked'); runInAction(() => { - doc.layoutKey = "layout_" + templateSignature; + doc.layoutKey = 'layout_' + templateSignature; createCustomView(doc, creator, templateSignature, docLayoutTemplate); }); batch.end(); @@ -1301,44 +1545,49 @@ export namespace DocUtils { } export function findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt<Doc>; - const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); + const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data); const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); - const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); + const allTemplates = iconViews + .concat(templBtns) + .concat(noteTypes) + .concat(clickFuncs) + .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) + .filter(doc => doc.isTemplateDoc); // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized // first try to find a template that matches the specific document type (<typeName>_<templateName>). otherwise, fallback to a general match on <templateName> - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc)); !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); return docLayoutTemplate; } - export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { - const templateName = templateSignature.replace(/\(.*\)/, ""); - docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? "transcription" : doc.type), templateSignature); + export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { + const templateName = templateSignature.replace(/\(.*\)/, ''); + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature); - const customName = "layout_" + templateSignature; + const customName = 'layout_' + templateSignature; const _width = NumCast(doc._width); const _height = NumCast(doc._height); - const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; + const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false }; if (docLayoutTemplate) { - if (docLayoutTemplate !== doc[customName]) { + if (docLayoutTemplate !== doc[customName]) { Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); } } else { let fieldTemplate: Opt<Doc>; - if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { - fieldTemplate = Docs.Create.TextDocument("", options); + if (doc.data instanceof RichTextField || typeof doc.data === 'string') { + fieldTemplate = Docs.Create.TextDocument('', options); } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); + fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); + fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); + fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); + fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); } - const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); } @@ -1350,13 +1599,14 @@ export namespace DocUtils { } } export function iconify(doc: Doc) { - const layoutKey = Cast(doc.layoutKey, "string", null); - DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined); - if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); + const layoutKey = Cast(doc.layoutKey, 'string', null); + DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); + if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', ''); } export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { - let w = 0, h = 0; + let w = 0, + h = 0; runInAction(() => { docList.forEach(d => { DocUtils.iconify(d); @@ -1364,19 +1614,23 @@ export namespace DocUtils { h = Math.max(NumCast(d._height), h); }); docList.forEach((d, i) => { - d.x = Math.cos(Math.PI * 2 * i / docList.length) * size; - d.y = Math.sin(Math.PI * 2 * i / docList.length) * size; - d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size; + d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size; + d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection }); - const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0); + const aggBounds = aggregateBounds( + docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), + 0, + 0 + ); docList.forEach((d, i) => { - d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2); - d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2); - d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + d.x = NumCast(d.x) - (aggBounds.r + aggBounds.x) / 2; + d.y = NumCast(d.y) - (aggBounds.b + aggBounds.y) / 2; + d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection }); }); if (create) { - const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, }); + const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2 }); newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size; newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size; newCollection._width = newCollection._height = size * 2; @@ -1388,18 +1642,25 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.isPushpin) return undefined; const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = DocListCast(doc.links). - some(l => - (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || - (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ - title: "pushpin", label: "", annotationOn: Cast(doc.annotationOn, Doc, null), isPushpin: true, - icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), backgroundColor: "#ACCEF7", - _width: 15, _height: 15, _xPadding: 0, _isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null) + title: 'pushpin', + label: '', + annotationOn: Cast(doc.annotationOn, Doc, null), + isPushpin: true, + icon: 'map-pin', + x: Cast(doc.x, 'number', null), + y: Cast(doc.y, 'number', null), + backgroundColor: '#ACCEF7', + _width: 15, + _height: 15, + _xPadding: 0, + _isLinkButton: true, + _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); - const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", ""); + const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', ''); doc._timecodeToShow = undefined; return pushpin; } @@ -1407,7 +1668,7 @@ export namespace DocUtils { } // /** - // * + // * // * @param dms Degree Minute Second format exif gps data // * @param ref ref that determines negativity of decimal coordinates // * @returns a decimal format of gps latitude / longitude @@ -1427,7 +1688,7 @@ export namespace DocUtils { function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { var dd = degrees + minutes / 60 + seconds / (60 * 60); - if (direction === "S" || direction === "W") { + if (direction === 'S' || direction === 'W') { dd = dd * -1; } // Don't do anything for N or E return dd; @@ -1444,15 +1705,18 @@ export namespace DocUtils { if (doc) { const proto = Doc.GetProto(doc); proto.text = result.rawText; - proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); + proto.fileUpload = pathname + .replace(/.*\//, '') + .replace('upload_', '') + .replace(/\.[a-z0-9]*$/, ''); if (Upload.isImageInformation(result)) { const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); - proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined); - proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim; - proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - if (NumCast(proto["data-nativeOrientation"]) >= 5) { - proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim; - proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (StrCast((result.exifData?.data as any)?.Orientation).includes('Rotate 90') ? 5 : undefined); + proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + if (NumCast(proto['data-nativeOrientation']) >= 5) { + proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); } proto.contentSize = result.contentSize; // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates @@ -1464,15 +1728,41 @@ export namespace DocUtils { proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); } - } generatedDocuments.push(doc); } } + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { + const tbox = Docs.Create.TextDocument('', { + _xMargin: noMargins ? 0 : undefined, + _yMargin: noMargins ? 0 : undefined, + annotationOn, + docMaxAutoHeight: maxHeight, + backgroundColor: backgroundColor, + _width: width || 200, + _height: 35, + x: x, + y: y, + _fitWidth: true, + _autoHeight: true, + title, + }); + const template = Doc.UserDoc().defaultTextLayout; + if (template instanceof Doc) { + tbox._width = NumCast(template._width); + tbox.layoutKey = 'layout_' + StrCast(template.title); + Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template; + } + return tbox; + } + export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) { const generatedDocuments: Doc[] = []; - for (const { source: { name, type }, result } of await Networking.UploadYoutubeToServer(videoId)) { + for (const { + source: { name, type }, + result, + } of await Networking.UploadYoutubeToServer(videoId)) { name && type && processFileupload(generatedDocuments, name, type, result, options); } return generatedDocuments; @@ -1480,7 +1770,10 @@ export namespace DocUtils { export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { const generatedDocuments: Doc[] = []; const upfiles = await Networking.UploadFilesToServer(files); - for (const { source: { name, type }, result } of upfiles) { + for (const { + source: { name, type }, + result, + } of upfiles) { name && type && processFileupload(generatedDocuments, name, type, result, options); } return generatedDocuments; @@ -1490,36 +1783,43 @@ export namespace DocUtils { export function copyDragFactory(dragFactory: Doc) { if (!dragFactory) return undefined; const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); + ndoc && Doc.AddDocToList(Doc.MyFileOrphans, 'data', Doc.GetProto(ndoc)); + if (ndoc && dragFactory['dragFactory-count'] !== undefined) { + dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1; + Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true); } - if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); + if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc); return ndoc; } export function delegateDragFactory(dragFactory: Doc) { const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); + if (ndoc && dragFactory['dragFactory-count'] !== undefined) { + dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1; + Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(); } return ndoc; } } -ScriptingGlobals.add("Docs", Docs); -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); }); -ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); }); -ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); +ScriptingGlobals.add('Docs', Docs); +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { + return DocUtils.copyDragFactory(dragFactory); +}); +ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { + return DocUtils.delegateDragFactory(dragFactory); +}); +ScriptingGlobals.add(function makeDelegate(proto: any) { + const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); + return d; +}); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { - const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : "<?>"; - const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>"; - const relation = self.linkRelationship || "to"; + const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : '<?>'; + const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : '<?>'; + const relation = self.linkRelationship || 'to'; return `${anchor1title} (${relation}) ${anchor2title}`; }); ScriptingGlobals.add(function openTabAlias(tab: Doc) { - CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right"); -});
\ No newline at end of file + CollectionDockingView.AddSplit(Doc.MakeAlias(tab), 'right'); +}); |