aboutsummaryrefslogtreecommitdiff
path: root/src/client/documents/Documents.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r--src/client/documents/Documents.ts490
1 files changed, 271 insertions, 219 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c7ea04839..c4014a752 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -2,12 +2,11 @@ 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 { Doc, DocListCast, 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';
@@ -31,16 +30,15 @@ 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 { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, 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 { DocFocusOptions, OpenWhere, OpenWhereMod } 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';
@@ -48,6 +46,7 @@ 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 { LoadingBox } from '../views/nodes/LoadingBox';
import { MapBox } from '../views/nodes/MapBox/MapBox';
import { PDFBox } from '../views/nodes/PDFBox';
import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
@@ -149,6 +148,8 @@ export class DocumentOptions {
_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)');
+ _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
+ _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
_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)');
@@ -156,18 +157,21 @@ export class DocumentOptions {
_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
- _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
+ _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target
_showTitle?: string; // field name to display in header (:hover is an optional suffix)
+ _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target
_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
_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)
+ enableDragWhenActive?: boolean; // allow dragging even if document contentts are active (e.g., tree, groups)
_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
+ viewType?: string; // sub type of a collection
_gridGap?: number; // gap between items in masonry view
_viewScale?: number; // how much a freeform view has been scaled (zoomed)
_overflow?: string; // set overflow behavior
@@ -197,6 +201,8 @@ export class DocumentOptions {
'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)');
+ openFactoryLocation?: string; // an OpenWhere value to place the factory created document
+ openFactoryAsDelegate?: boolean; //
lat?: number;
lng?: number;
infoWindowOpen?: boolean;
@@ -216,11 +222,17 @@ export class DocumentOptions {
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.
+ linkSource?: Doc; // the source document for a collection of backlinks
+ updateContentsScript?: ScriptField; // reactive script invoked when viewing a document that can update contents of a collection (or do anything)
toolTip?: string; // tooltip to display on hover
+ toolType?: string; // type of pen tool
+ expertMode?: boolean; // something available only in expert (not novice) mode
contextMenuFilters?: List<ScriptField>;
contextMenuScripts?: List<ScriptField>;
contextMenuLabels?: List<string>;
contextMenuIcons?: List<string>;
+ defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen
+ waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait
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
@@ -235,7 +247,9 @@ export class DocumentOptions {
childContextMenuScripts?: List<ScriptField>;
childContextMenuLabels?: List<string>;
childContextMenuIcons?: List<string>;
+ followLinkZoom?: boolean; // whether to zoom to the target of a link
hideLinkButton?: boolean; // whether the blue link counter button should be hidden
+ disableDocBrushing?: boolean; // whether to suppress border highlighting
hideDecorationTitle?: boolean;
hideOpenButton?: boolean;
hideResizeHandles?: boolean;
@@ -251,15 +265,20 @@ export class DocumentOptions {
defaultBackgroundColor?: string;
_isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked
_linkAutoMove?: boolean; // whether link endpoint should move around the edges of a document to make shortest path to other link endpoint
+ hideLinkAnchors?: boolean; // suppresses link anchor dots from being displayed
isFolder?: boolean;
lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide)
activeFrame?: number; // the active frame of a document in a frame base collection
appearFrame?: number; // the frame in which the document appears
+ viewTransitionTime?: number; // transition duration for view parameters
+ presPanX?: number; // panX saved as a view spec
+ presPanY?: number; // panY saved as a view spec
+ presViewScale?: number; // viewScale saved as a view Spec
presTransition?: number; //the time taken for the transition TO a document
presDuration?: number; //the duration of the slide in presentation view
- presProgressivize?: boolean;
+ presZoomText?: boolean; // whether text anchors should shown in a larger box when following links to make them stand out
borderRounding?: string;
- boxShadow?: string;
+ boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
data?: any;
baseProto?: boolean; // is this a base prototoype
dontRegisterView?: boolean;
@@ -269,7 +288,7 @@ export class DocumentOptions {
clipWidth?: number; // percent transition from before to after in comparisonBox
dockingConfig?: string;
annotationOn?: Doc;
- isPushpin?: boolean;
+ followLinkToggle?: boolean;
isGroup?: boolean; // whether a collection should use a grouping UI behavior
_removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
noteType?: string;
@@ -296,7 +315,7 @@ export class DocumentOptions {
linearViewExpandable?: boolean; // can linear view be expanded
linearViewToggleButton?: string; // button to open close linear view group
linearViewSubMenu?: boolean;
- linearViewFloating?: boolean;
+ linearBtnWidth?: number;
flexGap?: number; // Linear view flex gap
flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
@@ -330,10 +349,10 @@ export class DocumentOptions {
strokeWidth?: number;
freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection
treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view
+ treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'unrendered' tag unless they also have a treeViewFieldKey tag (presBox)
treeViewHideHeaderIfTemplate?: boolean; // whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox)
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; //
// Action Button
buttonMenu?: boolean; // whether a action button should be displayed
@@ -393,9 +412,9 @@ export namespace Docs {
_xMargin: 10,
_yMargin: 10,
nativeDimModifiable: true,
- treeViewGrowsHorizontally: true,
+ nativeHeightUnfrozen: true,
forceReflow: true,
- links: '@links(self)',
+ defaultDoubleClick: 'ignore',
},
},
],
@@ -403,42 +422,35 @@ export namespace Docs {
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)' },
+ options: { _width: 400 },
},
],
[
DocumentType.COLOR,
{
layout: { view: ColorBox, dataField: defaultDataKey },
- options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' },
+ options: { _nativeWidth: 220, _nativeHeight: 300 },
},
],
[
DocumentType.IMG,
{
layout: { view: ImageBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.WEB,
{
layout: { view: WebBox, dataField: defaultDataKey },
- options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+ options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, waitForDoubleClickToClick: 'always' },
},
],
[
DocumentType.COL,
{
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' },
+ options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 },
},
],
[
@@ -452,35 +464,35 @@ export namespace Docs {
DocumentType.VID,
{
layout: { view: VideoBox, dataField: defaultDataKey },
- options: { _currentTimecode: 0, links: '@links(self)' },
+ options: { _currentTimecode: 0 },
},
],
[
DocumentType.AUDIO,
{
layout: { view: AudioBox, dataField: defaultDataKey },
- options: { _height: 100, backgroundColor: 'lightGray', _fitWidth: true, forceReflow: true, nativeDimModifiable: true, links: '@links(self)' },
+ options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true },
},
],
[
DocumentType.REC,
{
layout: { view: VideoBox, dataField: defaultDataKey },
- options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' },
+ options: { _height: 100, backgroundColor: 'pink' },
},
],
[
DocumentType.PDF,
{
layout: { view: PDFBox, dataField: defaultDataKey },
- options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+ options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true },
},
],
[
DocumentType.MAP,
{
layout: { view: MapBox, dataField: defaultDataKey },
- options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' },
+ options: { _height: 600, _width: 800, nativeDimModifiable: true },
},
],
[
@@ -497,11 +509,11 @@ export namespace Docs {
options: {
childDontRegisterViews: true,
_isLinkButton: true,
+ hideLinkAnchors: 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']),
},
},
@@ -526,7 +538,7 @@ export namespace Docs {
DocumentType.SCRIPTING,
{
layout: { view: ScriptingBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
@@ -539,69 +551,70 @@ export namespace Docs {
DocumentType.LABEL,
{
layout: { view: LabelBox, dataField: defaultDataKey },
- options: { links: '@links(self)', _singleLine: true },
+ options: { _singleLine: true },
},
],
[
DocumentType.EQUATION,
{
layout: { view: EquationBox, dataField: defaultDataKey },
- options: { links: '@links(self)', nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true },
+ options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true },
},
],
[
DocumentType.FUNCPLOT,
{
layout: { view: FunctionPlotBox, dataField: defaultDataKey },
- options: { nativeDimModifiable: true, links: '@links(self)' },
+ options: { nativeDimModifiable: true },
},
],
[
DocumentType.BUTTON,
{
layout: { view: LabelBox, dataField: 'onClick' },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.SLIDER,
{
layout: { view: SliderBox, dataField: defaultDataKey },
- options: { links: '@links(self)', treeViewGrowsHorizontally: true },
+ options: {},
},
],
[
DocumentType.PRES,
{
layout: { view: PresBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: { defaultDoubleClick: 'ignore', hideLinkAnchors: true },
},
],
[
DocumentType.FONTICON,
{
- layout: { view: FontIconBox, dataField: defaultDataKey },
- options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' },
+ layout: { view: FontIconBox, dataField: 'icon' },
+ options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', enableDragWhenActive: true, hideLinkButton: true, _width: 40, _height: 40 },
},
],
[
DocumentType.WEBCAM,
{
layout: { view: RecordingBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.PRESELEMENT,
{
layout: { view: PresElementBox, dataField: defaultDataKey },
+ options: { title: 'pres element template', _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' },
},
],
[
DocumentType.MARKER,
{
layout: { view: CollectionView, dataField: defaultDataKey },
- options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' },
+ options: { hideLinkButton: true, pointerEvents: 'none' },
},
],
[
@@ -609,21 +622,21 @@ export namespace Docs {
{
// NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
layout: { view: InkingStroke, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.SCREENSHOT,
{
layout: { view: ScreenshotBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.COMPARISON,
{
layout: { view: ComparisonBox, dataField: defaultDataKey },
- options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' },
+ options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' },
},
],
[
@@ -638,14 +651,21 @@ export namespace Docs {
DocumentType.GROUP,
{
layout: { view: EmptyBox, dataField: defaultDataKey },
- options: { links: '@links(self)' },
+ options: {},
},
],
[
DocumentType.DATAVIZ,
{
layout: { view: DataVizBox, dataField: defaultDataKey },
- options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
+ options: { _fitWidth: true, nativeDimModifiable: true },
+ },
+ ],
+ [
+ DocumentType.LOADING,
+ {
+ layout: { view: LoadingBox, dataField: '' },
+ options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true },
},
],
]);
@@ -664,8 +684,6 @@ export namespace Docs {
* haven't been initialized, the newly initialized prototype document.
*/
export async function initialize(): Promise<void> {
- ProxyField.initPlugin();
- ComputedField.initPlugin();
// non-guid string ids for each document prototype
const prototypeIds = Object.values(DocumentType)
.filter(type => type !== DocumentType.NONE)
@@ -752,7 +770,6 @@ export namespace Docs {
...(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('@')) {
@@ -786,49 +803,63 @@ 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) {
+ function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) {
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-Override'] = SharingPermissions.Unset;
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();
+ if (fieldKey) {
+ 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] = data;
+ // users placeholderDoc as proto if it exists
+ const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
- // 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>();
- const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
+ if (placeholderDoc) {
+ dataDoc.proto = proto;
+ }
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-Override'] = SharingPermissions.Unset;
viewFirstProps.author = Doc.CurrentUserEmail;
- const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
+ let viewDoc: Doc;
+ // determines whether viewDoc should be created using placeholder Doc or default
+ if (placeholderDoc) {
+ placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
+ placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
+ viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+ } else {
+ 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);
+ if (![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(Doc.MyFileOrphans, undefined, dataDoc);
+ Doc.AddFileOrphan(dataDoc);
updateCachedAcls(dataDoc);
updateCachedAcls(viewDoc);
+
return viewDoc;
}
- export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) {
+ export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) {
const imgField = url instanceof ImageField ? url : new ImageField(url);
- return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
+ return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { _nativeDimModifiable: false, _nativeHeightUnfrozen: false, title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc);
}
export function PresDocument(options: DocumentOptions = {}) {
@@ -839,12 +870,12 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
}
- export function VideoDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options);
+ export function VideoDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+ return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options, undefined, undefined, undefined, overwriteDoc);
}
- export function YoutubeDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options);
+ export function YoutubeDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+ return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options, undefined, undefined, undefined, overwriteDoc);
}
export function WebCamDocument(url: string, options: DocumentOptions = {}) {
@@ -859,8 +890,8 @@ export namespace Docs {
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 });
+ export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
+ return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), options, undefined, undefined, undefined, overwriteDoc);
}
export function RecordingDocument(url: string, options: DocumentOptions = {}) {
@@ -874,6 +905,9 @@ export namespace Docs {
export function ColorDocument(options: DocumentOptions = {}) {
return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
}
+ export function LoadingDocument(file: File | string, options: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, '');
+ }
export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
@@ -901,13 +935,13 @@ export namespace Docs {
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) {
+ export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) {
const linkDoc = InstanceFromProto(
Prototypes.get(DocumentType.LINK),
undefined,
{
- anchor1: source.doc,
- anchor2: target.doc,
+ anchor1: source,
+ anchor2: target,
...options,
},
id
@@ -918,9 +952,22 @@ export namespace Docs {
return linkDoc;
}
- export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) {
+ export function InkDocument(
+ color: string,
+ tool: string,
+ strokeWidth: number,
+ strokeBezier: string,
+ fillColor: string,
+ arrowStart: string,
+ arrowEnd: string,
+ dash: string,
+ points: PointData[],
+ isInkMask: boolean,
+ options: DocumentOptions = {}
+ ) {
const I = new Doc();
I[Initializing] = true;
+ I.isInkMask = isInkMask;
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString('data');
I.color = color;
@@ -933,6 +980,7 @@ export namespace Docs {
I.strokeEndMarker = arrowEnd;
I.strokeDash = dash;
I.tool = tool;
+ I.fitWidth = true;
I['text-align'] = 'center';
I.title = 'ink';
I.x = options.x as number;
@@ -943,21 +991,21 @@ export namespace Docs {
I.author = Doc.CurrentUserEmail;
I.rotation = 0;
I.data = new InkField(points);
+ I.defaultDoubleClick = 'click';
I.creationDate = new DateField();
I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
- I['acl-Override'] = 'None';
- I.links = ComputedField.MakeFunction('links(self)');
+ //I['acl-Override'] = SharingPermissions.Unset;
I[Initializing] = false;
return I;
}
- export function PdfDocument(url: string, options: DocumentOptions = {}) {
+ export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) {
const width = options._width || undefined;
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.PDF), new PdfField(url), options);
+ return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options, undefined, undefined, undefined, overwriteDoc);
}
export function WebDocument(url: string, options: DocumentOptions = {}) {
@@ -981,9 +1029,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), { lat, lng, infoWindowOpen, ...options }, id);
}
- export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options });
- }
+ // shouldn't ever need to create a KVP document-- instead set the LayoutTemplateString to be a KeyValueBox for the DocumentView (see addDocTab in TabDocView)
+ // export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
+ // 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);
@@ -995,10 +1044,21 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.MARKER), url, options, id);
}
+ export function CollectionAnchorDocument(options: DocumentOptions = {}, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+ }
export function TextanchorDocument(options: DocumentOptions = {}, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
}
+ export function ImageanchorDocument(options: DocumentOptions = {}, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+ }
+
+ export function InkAnchorDocument(options: DocumentOptions, id?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.MARKER), options?.data, options, id);
+ }
+
export function HTMLAnchorDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
return InstanceFromProto(Prototypes.get(DocumentType.MARKER), new List(documents), options, id);
}
@@ -1084,8 +1144,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) });
}
- export function PresElementBoxDocument(options?: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
+ export function PresElementBoxDocument() {
+ return Prototypes.get(DocumentType.PRESELEMENT);
}
export function DataVizDocument(url: string, options?: DocumentOptions) {
@@ -1093,7 +1153,7 @@ export namespace Docs {
}
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, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
}
export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -1130,30 +1190,6 @@ 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
- docFilters.forEach(filter => {
- const fields = filter.split(':');
- const key = fields[0];
- const value = fields[1];
- const modifiers = fields[2];
- if (!filterFacets[key]) {
- filterFacets[key] = {};
- }
- filterFacets[key][value] = modifiers;
- });
-
- 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 failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
- if (failsNotEqualFacets) {
- return true;
- }
- }
- return false;
- }
/**
* @param docs
* @param docFilters
@@ -1187,7 +1223,7 @@ export namespace DocUtils {
return false;
}
- for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) {
+ for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(':')[0])) {
const facet = filterFacets[facetKey];
// facets that match some value in the field of the document (e.g. some text field)
@@ -1223,7 +1259,7 @@ export namespace DocUtils {
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 (parentCollection?.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
@@ -1249,58 +1285,24 @@ export namespace DocUtils {
return rangeFilteredDocs;
}
- export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
- targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, '');
- DocServer.GetRefField(targetID).then(doc => {
- if (promoteDoc !== doc) {
- let copy = doc as Doc;
- if (copy) {
- Doc.Overwrite(promoteDoc, copy, true);
- } else {
- copy = Doc.MakeCopy(promoteDoc, true, targetID);
- }
- !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID);
- addDoc && addDoc(copy);
- 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);
- }
- });
- });
- }
- }
- });
- }
-
- export function DefaultFocus(doc: Doc, options?: DocFocusOptions) {
- options?.afterFocus?.(false);
+ export function DefaultFocus(doc: Doc, options: DocFocusOptions) {
+ return undefined;
}
- export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = [];
+ export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = [];
export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) {
broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
return DocUtils.ActiveRecordings.map(audio => {
const sourceDoc = getSourceDoc();
- const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
- link && (link.followLinkLocation = 'add:right');
- return link;
+ return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' });
});
}
- 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;
+ export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+ if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
+ const sv = DocumentManager.Instance.getDocumentView(source);
+ if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return;
if (target.doc === Doc.UserDoc()) return undefined;
const makeLink = action((linkDoc: Doc, showPopup?: number[]) => {
@@ -1340,17 +1342,16 @@ export namespace DocUtils {
target,
{
title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
- 'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined,
- 'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined,
+ 'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined,
+ 'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined,
'acl-Public': SharingPermissions.Augment,
'_acl-Public': SharingPermissions.Augment,
- linkDisplay: true,
- _hidden: true,
+ linkDisplay: linkSettings.linkDisplay,
_linkAutoMove: true,
- linkRelationship,
+ linkRelationship: linkSettings.linkRelationship,
_showCaption: 'description',
_showTitle: 'linkRelationship',
- description,
+ description: linkSettings.description,
},
id
),
@@ -1358,24 +1359,39 @@ export namespace DocUtils {
);
}
- export function AssignScripts(doc: Doc, scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
+ export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, 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', thisContainer: Doc.name, documentView: Doc.name, heading: Doc.name, checked: 'boolean', containingTreeView: Doc.name },
- { _readOnly_: true }
- );
+ const script = scripts[key];
+ if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) {
+ doc[key] = ScriptField.MakeScript(script, {
+ self: Doc.name,
+ this: Doc.name,
+ dragData: DragManager.DocumentDragData.name,
+ value: 'any',
+ _readOnly_: 'boolean',
+ scriptContext: 'any',
+ thisContainer: Doc.name,
+ documentView: Doc.name,
+ heading: Doc.name,
+ checked: 'boolean',
+ containingTreeView: Doc.name,
+ altKey: 'boolean',
+ ctrlKey: 'boolean',
+ shiftKey: 'boolean',
+ });
}
});
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 });
- }
- });
+ Object.keys(funcs)
+ .filter(key => !key.endsWith('-setter'))
+ .map(key => {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) {
+ const setFunc = Cast(funcs[key + '-setter'], 'string', null);
+ doc[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined;
+ }
+ });
return doc;
}
export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) {
@@ -1403,49 +1419,55 @@ export namespace DocUtils {
export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined {
let created: Doc | undefined;
- let layout: ((fieldKey: string) => string) | undefined;
const field = target[fieldKey];
- const resolved = options || {};
+ const resolved = options ?? {};
if (field instanceof ImageField) {
created = Docs.Create.ImageDocument(field.url.href, resolved);
- layout = ImageBox.LayoutString;
+ created.layout = ImageBox.LayoutString(fieldKey);
} else if (field instanceof Doc) {
created = field;
} else if (field instanceof VideoField) {
created = Docs.Create.VideoDocument(field.url.href, resolved);
- layout = VideoBox.LayoutString;
+ created.layout = VideoBox.LayoutString(fieldKey);
} else if (field instanceof PdfField) {
created = Docs.Create.PdfDocument(field.url.href, resolved);
- layout = PDFBox.LayoutString;
+ created.layout = PDFBox.LayoutString(fieldKey);
} else if (field instanceof AudioField) {
created = Docs.Create.AudioDocument(field.url.href, resolved);
- layout = AudioBox.LayoutString;
+ created.layout = AudioBox.LayoutString(fieldKey);
} else if (field instanceof RecordingField) {
created = Docs.Create.RecordingDocument(field.url.href, resolved);
- layout = RecordingBox.LayoutString;
+ created.layout = RecordingBox.LayoutString(fieldKey);
} else if (field instanceof InkField) {
- created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved);
- layout = InkingStroke.LayoutString;
+ created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved);
+ created.layout = InkingStroke.LayoutString(fieldKey);
} else if (field instanceof List && field[0] instanceof Doc) {
created = Docs.Create.StackingDocument(DocListCast(field), resolved);
- layout = CollectionView.LayoutString;
+ created.layout = CollectionView.LayoutString(fieldKey);
} else if (field instanceof MapField) {
created = Docs.Create.MapDocument(DocListCast(field), resolved);
- layout = MapBox.LayoutString;
+ created.layout = MapBox.LayoutString(fieldKey);
} else {
created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
- layout = FormattedTextBox.LayoutString;
+ created.layout = FormattedTextBox.LayoutString(fieldKey);
}
if (created) {
- created.layout = layout?.(fieldKey);
created.title = fieldKey;
proto && created.proto && (created.proto = Doc.GetProto(proto));
}
return created;
}
- 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;
+ /**
+ *
+ * @param type the type of file.
+ * @param path the path to the file.
+ * @param options the document options.
+ * @param overwriteDoc the placeholder loading doc.
+ * @returns
+ */
+ export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise<Opt<Doc>> {
+ let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise<Doc | undefined>) | undefined = undefined;
if (type.indexOf('image') !== -1) {
ctor = Docs.Create.ImageDocument;
if (!options._width) options._width = 300;
@@ -1494,7 +1516,7 @@ export namespace DocUtils {
options = { ...options, _width: 400, _height: 512, title: path };
}
- return ctor ? ctor(path, options) : undefined;
+ return ctor ? ctor(path, options, overwriteDoc) : undefined;
}
export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void {
@@ -1525,7 +1547,7 @@ export namespace DocUtils {
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)
+ .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail)
.map((dragDoc, i) => ({
description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
event: undoBatch((args: { x: number; y: number }) => {
@@ -1564,7 +1586,7 @@ export namespace DocUtils {
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 clickFuncs = DocListCast(Cast(Doc.UserDoc()['template-clickFuncs'], Doc, null)?.data);
const allTemplates = iconViews
.concat(templBtns)
.concat(noteTypes)
@@ -1656,19 +1678,20 @@ export namespace DocUtils {
}
export function LeavePushpin(doc: Doc, annotationField: string) {
- if (doc.isPushpin) return undefined;
+ if (doc.followLinkToggle) 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 = LinkManager.Links(doc).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,
+ followLinkToggle: true,
icon: 'map-pin',
x: Cast(doc.x, 'number', null),
y: Cast(doc.y, 'number', null),
backgroundColor: '#ACCEF7',
+ hideAllLinks: true,
_width: 15,
_height: 15,
_xPadding: 0,
@@ -1676,7 +1699,7 @@ export namespace DocUtils {
_timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
});
Doc.AddDocToList(context, annotationField, pushpin);
- const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', '');
+ const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, '');
doc._timecodeToShow = undefined;
return pushpin;
}
@@ -1710,14 +1733,14 @@ export namespace DocUtils {
return dd;
}
- async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions) {
+ async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) {
if (result instanceof Error) {
alert(`Upload failed: ${result.message}`);
return;
}
const full = { ...options, _width: 400, title: name };
const pathname = Utils.prepend(result.accessPaths.agnostic.client);
- const doc = await DocUtils.DocumentFromType(type, pathname, full);
+ const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc);
if (doc) {
const proto = Doc.GetProto(doc);
proto.text = result.rawText;
@@ -1727,7 +1750,8 @@ export namespace DocUtils {
.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);
+ const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase();
+ proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 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) {
@@ -1745,6 +1769,14 @@ export namespace DocUtils {
proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
}
}
+ if (Upload.isVideoInformation(result)) {
+ proto['data-duration'] = result.duration;
+ }
+ if (overwriteDoc) {
+ Doc.removeCurrentlyLoading(overwriteDoc);
+ // loading doc icons are just labels. so any icon views of loading docs need to be replaced with the proper icon view.
+ DocumentManager.Instance.getAllDocumentViews(overwriteDoc).forEach(dv => StrCast(dv.rootDoc.layoutKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
+ }
generatedDocuments.push(doc);
}
}
@@ -1755,7 +1787,7 @@ export namespace DocUtils {
_yMargin: noMargins ? 0 : undefined,
annotationOn,
docMaxAutoHeight: maxHeight,
- backgroundColor: backgroundColor,
+ backgroundColor,
_width: width || 200,
_height: 35,
x: x,
@@ -1773,16 +1805,23 @@ export namespace DocUtils {
return tbox;
}
- export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) {
+ export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) {
const generatedDocuments: Doc[] = [];
- for (const {
- source: { name, type },
- result,
- } of await Networking.UploadYoutubeToServer(videoId)) {
- name && type && processFileupload(generatedDocuments, name, type, result, options);
- }
- return generatedDocuments;
+ Networking.UploadYoutubeToServer(videoId).then(upfiles => {
+ const {
+ source: { name, type },
+ result,
+ } = upfiles.lastElement();
+ if ((result as any).message) {
+ if (overwriteDoc) {
+ overwriteDoc.isLoading = false;
+ overwriteDoc.loadingError = (result as any).message;
+ Doc.removeCurrentlyLoading(overwriteDoc);
+ }
+ } else name && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc);
+ });
}
+
export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
const generatedDocuments: Doc[] = [];
const upfiles = await Networking.UploadFilesToServer(files);
@@ -1795,11 +1834,27 @@ export namespace DocUtils {
return generatedDocuments;
}
+ export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) {
+ const generatedDocuments: Doc[] = [];
+ Networking.UploadFilesToServer([file]).then(upfiles => {
+ const {
+ source: { name, type },
+ result,
+ } = upfiles.lastElement() ?? { source: { name: '', type: '' }, result: { message: 'upload failed' } };
+ if ((result as any).message) {
+ if (overwriteDoc) {
+ overwriteDoc.loadingError = (result as any).message;
+ Doc.removeCurrentlyLoading(overwriteDoc);
+ }
+ } else name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc);
+ });
+ }
+
// copies the specified drag factory document
export function copyDragFactory(dragFactory: Doc) {
if (!dragFactory) return undefined;
const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
- ndoc && Doc.AddDocToList(Doc.MyFileOrphans, 'data', Doc.GetProto(ndoc));
+ ndoc && Doc.AddFileOrphan(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);
@@ -1820,22 +1875,19 @@ export namespace DocUtils {
}
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 copyDragFactory(dragFactory: Doc, asDelegate?: boolean) {
+ return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : 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 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');
+ CollectionDockingView.AddSplit(Doc.MakeAlias(tab), OpenWhereMod.right);
});