From a6d904bcd18a2c9962abfd9b5b325340f6b18b0d Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 9 Feb 2022 08:59:32 -0500 Subject: speeding up rendering using bitmaps for webpages and other heavyweight docs. --- src/.DS_Store | Bin 8196 -> 8196 bytes src/client/documents/Documents.ts | 64 ++-- src/client/util/CurrentUserUtils.ts | 64 ++-- src/client/util/Scripting.ts | 1 + src/client/views/PropertiesButtons.tsx | 19 + .../collections/collectionFreeForm/MarqueeView.tsx | 23 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 9 + src/client/views/nodes/DocumentView.tsx | 15 +- src/client/views/nodes/VideoBox.tsx | 5 +- src/client/views/nodes/WebBox.tsx | 61 +++- src/client/views/nodes/WebBoxRenderer.js | 395 +++++++++++++++++++++ src/fields/Doc.ts | 5 +- src/fields/ScriptField.ts | 32 +- src/fields/Types.ts | 7 + src/server/ApiManagers/UploadManager.ts | 5 +- src/server/server_Initialization.ts | 20 +- 16 files changed, 624 insertions(+), 101 deletions(-) create mode 100644 src/client/views/nodes/WebBoxRenderer.js (limited to 'src') diff --git a/src/.DS_Store b/src/.DS_Store index bdc161da2..04487bc70 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index df573a377..0cb4f4c5e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -361,31 +361,34 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, - options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { + _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, + links: "@links(self)" + } }], [DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any } + options: { _width: 400, links: "@links(self)" } }], [DocumentType.FILTER, { layout: { view: FilterBox, dataField: defaultDataKey }, - options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any } + options: { _width: 400, links: "@links(self)" } }], [DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, - options: { _nativeWidth: 220, _nativeHeight: 300, links: ComputedField.MakeFunction("links(self)") as any } + options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" } }], [DocumentType.IMG, { layout: { view: ImageBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } + 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: ComputedField.MakeFunction("links(self)") as any } + options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" } }], [DocumentType.KVP, { layout: { view: KeyValueBox, dataField: defaultDataKey }, @@ -393,19 +396,19 @@ export namespace Docs { }], [DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0, links: ComputedField.MakeFunction("links(self)") as any }, + options: { _currentTimecode: 0, links: "@links(self)" }, }], [DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: "lightGray", links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" } }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" } }], [DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 600, _width: 800, links: "@links(self)" } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -416,7 +419,7 @@ export namespace Docs { 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: ComputedField.MakeFunction("links(self)") as any, + links: "@links(self)", _removeDropProperties: new List(["_layerTags", "isLinkButton"]), } }], @@ -432,61 +435,61 @@ export namespace Docs { }], [DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.YOUTUBE, { layout: { view: YoutubeBox, dataField: defaultDataKey } }], [DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any, hideResizeHandles: true, hideDecorationTitle: true } + options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true } }], [DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.BUTTON, { layout: { view: LabelBox, dataField: "onClick" }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.SLIDER, { layout: { view: SliderBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: ComputedField.MakeFunction("links(self)") as any }, + options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" }, }], [DocumentType.WEBCAM, { layout: { view: DashWebRTCVideo, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.PRESELEMENT, { layout: { view: PresElementBox, dataField: defaultDataKey } }], [DocumentType.MARKER, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true } + options: { links: "@links(self)", hideLinkButton: true } }], [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], [DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: ComputedField.MakeFunction("links(self)") as any } + options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } }], [DocumentType.GROUPDB, { data: new List(), @@ -495,7 +498,7 @@ export namespace Docs { }], [DocumentType.GROUP, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: "@links(self)" } }], ]); @@ -591,7 +594,12 @@ export namespace Docs { 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("") - }; + } + Object.entries(options).map(pair => { + if (typeof pair[1] === "string" && pair[1].startsWith("@")) { + (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1)); + } + }); return Doc.assign(new Doc(prototypeId, true), options as any, undefined, true); } } @@ -758,7 +766,7 @@ export namespace Docs { I.data = new InkField(points); I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; - I.links = ComputedField.MakeFunction("links(self)") as any; + I.links = "@links(self)"; I[Initializing] = false; return I; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a8b0da369..9ecb2c277 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -162,37 +162,37 @@ export class CurrentUserUtils { }); } - if (doc["template-button-switch"] === undefined) { - const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; - - const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); - const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); - const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); - const labelTemplate = { - doc: { - type: "doc", content: [{ - type: "paragraph", - content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] - }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); - Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); - // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); - // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); - Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); - // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); - const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); - box.isTemplateDoc = makeTemplate(box, true, "switch"); - - doc["template-button-switch"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, - btnType: ButtonType.ToolButton - }); - } + // if (doc["template-button-switch"] === undefined) { + // const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; + + // const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); + // const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); + // const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); + // const labelTemplate = { + // doc: { + // type: "doc", content: [{ + // type: "paragraph", + // content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] + // }] + // }, + // selection: { type: "text", anchor: 1, head: 1 }, + // storedMarks: [] + // }; + // Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); + // Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); + // // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); + // // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); + // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); + // // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); + // const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); + // box.isTemplateDoc = makeTemplate(box, true, "switch"); + + // doc["template-button-switch"] = CurrentUserUtils.createToolButton({ + // onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + // dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, + // btnType: ButtonType.ToolButton + // }); + // } if (doc["template-button-detail"] === undefined) { const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; @@ -1010,7 +1010,7 @@ export class CurrentUserUtils { static inkTools(doc: Doc) { const tools: Button[] = [ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser")', checkResult: 'setActiveInkTool("eraser" , true)' }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser")', checkResult: 'setActiveInkTool("eraser", true)' }, // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' }, diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40b94024e..66cc7766f 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -269,6 +269,7 @@ function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, inde } export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { + console.log("script = " + script); const { requiredType = "", addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options; if (options.params && !options.params.this) options.params.this = Doc.name; if (options.params && !options.params.self) options.params.self = Doc.name; diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index f9dab9f82..dd6448654 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -18,6 +18,10 @@ import React = require("react"); import { Colors } from "./global/globalEnums"; import { CollectionFreeFormView } from "./collections/collectionFreeForm"; import { DocumentManager } from "../util/DocumentManager"; +import { pasteImageBitmap } from "./nodes/WebBoxRenderer"; +import { VideoBox } from "./nodes/VideoBox"; +import { Id } from "../../fields/FieldSymbols"; +import { ImageField } from "../../fields/URLField"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -92,6 +96,20 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get groupButton() { return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; }); } + @computed get freezeThumb() { + return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => { + if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined; + else { + document.body.focus(); // so that we can access the clipboard without an error + setTimeout(() => + pasteImageBitmap((data: any, error: any) => { + error && console.log(error); + data && VideoBox.convertDataUri(data, doc[Id] + "-thumb-frozen", true).then( + returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename)); + })); + } + }); + } @computed get snapButton() { return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true); } @@ -222,6 +240,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.dictationButton, { display: isNovice ? "none" : "" })} {toggle(this.onClickButton)} {toggle(this.fitWidthButton)} + {toggle(this.freezeThumb)} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })} {toggle(this.maskButton, { display: !isInk ? "none" : "" })} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 08da682bb..b10b0912f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -30,6 +30,9 @@ import "./MarqueeView.scss"; import React = require("react"); import { StyleLayers } from "../../StyleProvider"; import { TreeView } from "../TreeView"; +import { VideoBox } from "../../nodes/VideoBox"; +import { ImageField, WebField } from "../../../../fields/URLField"; +import { pasteImageBitmap } from "../../nodes/WebBoxRenderer"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -134,17 +137,15 @@ export class MarqueeView extends React.Component { - // const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); - // if (ns.length === 1 && text.startsWith("http")) { - // this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer - // } else { - // this.pasteTable(ns, x, y); - // } - // }); - // e.stopPropagation(); - + document.body.focus(); // so that we can access the clipboard without an error + setTimeout(() => + pasteImageBitmap((data: any, error: any) => { + error && console.log(error); + data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => { + this.props.Document["thumb-frozen"] = new ImageField(returnedfilename); + }); + })); + } else if (e.key === "s" && e.ctrlKey) { e.preventDefault(); const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!; slide.x = x; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fe34d6687..28a65f628 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -154,6 +154,14 @@ export class CollectionFreeFormDocumentView extends DocComponent this; render() { TraceMobx(); + + const panx = this.props.CollectionFreeFormView.panX(); + const pany = this.props.CollectionFreeFormView.panY(); + const viewWidth = this.props.CollectionFreeFormView.props.PanelWidth() / this.props.CollectionFreeFormView.zoomScaling() / 2; + const viewHeight = this.props.CollectionFreeFormView.props.PanelHeight() / this.props.CollectionFreeFormView.zoomScaling() / 2; + const hideContent = !this.props.CollectionFreeFormView.props.isAnnotationOverlay && + (Math.min(Math.abs(panx - (this.X + this.panelWidth())), Math.abs(panx - (this.X))) > viewWidth || + Math.min(Math.abs(pany - (this.Y + this.panelHeight())), Math.abs(pany - (this.Y))) > viewHeight) ? true : false; const divProps: DocumentViewProps = { ...this.props, CollectionFreeFormDocumentView: this.returnThis, @@ -162,6 +170,7 @@ export class CollectionFreeFormDocumentView extends DocComponent disposer?.()); } handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent): any => { @@ -837,6 +840,7 @@ export class DocumentViewInternal extends DocComponent + {(this.isContentActive() && !SnappingManager.GetIsDragging()) || !thumb ? (null) : + ; const WebDocument = makeInterface(documentSchema); @@ -76,6 +78,7 @@ export class WebBox extends ViewBoxAnnotatableComponent a.textInlineAnnotations); } @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; } + @computed get webThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; } constructor(props: any) { super(props); @@ -108,9 +111,47 @@ export class WebBox extends ViewBoxAnnotatableComponent { this._annotationKeySuffix = () => this._urlHash + "-annotations"; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) - this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`); - this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`); + this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.GetAnnoCopyField(this.fieldKey); + this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.GetSidebarCopyField(this.fieldKey); }); + reaction(() => this.props.isSelected(), + async (selected) => { + if (selected) { + setTimeout(action(() => { + this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0); + if (this._initialScroll !== undefined && this._outerRef.current) { + setTimeout(() => { + this._outerRef.current!.scrollTop = this._initialScroll!; + this._initialScroll = undefined; + }); + } + })); + } else if (!this.props.isContentActive()) { + const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href; + if (this._iframe && !imageBitmap) { + var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); + if (!htmlString) { + htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text(); + } + const nativeWidth = NumCast(this.layoutDoc.nativeWidth); + CreateImage( + this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, + this._iframe.contentDocument?.styleSheets ?? [], + htmlString, + nativeWidth, + nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), + NumCast(this.layoutDoc._scrollTop) + ).then + (action((dataUrl: any) => { + VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb", true).then( + returnedfilename => this.layoutDoc.thumb = new ImageField(returnedfilename)); + })) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + } + } + }); this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight, autoHeight => { @@ -292,12 +333,6 @@ export class WebBox extends ViewBoxAnnotatableComponent this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); - const initialScroll = this._initialScroll; - if (initialScroll !== undefined && this._outerRef.current) { - // bcz: not sure why this happens, but if the webpage isn't ready yet, it's scroll height seems to be limited. So we need to wait tp set scroll location to what we want. - setTimeout(() => this._outerRef.current!.scrollTop = initialScroll); - this._initialScroll = undefined; - } iframe.setAttribute("enable-annotation", "true"); iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; @@ -665,7 +700,8 @@ export class WebBox extends ViewBoxAnnotatableComponent -
+