diff options
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 14 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 77 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 39 |
4 files changed, 113 insertions, 23 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index f23c68409..750213e67 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -88,15 +88,21 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl </div>; }; const displayDoc = (which: string) => { - var whichDoc = Cast(this.dataDoc[which], Doc, null); - if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null); + const whichDoc = Cast(this.dataDoc[which], Doc, null); + //if (whichDoc?.type === DocumentType.MARKER) + const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc; return whichDoc ? <> - <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + <DocumentView + ref={(r) => { + whichDoc !== targetDoc && r?.focus(targetDoc); + }} + {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} isContentActive={returnFalse} isDocumentActive={returnFalse} styleProvider={this.docStyleProvider} - Document={whichDoc} + Document={targetDoc} DataDoc={undefined} + hideLinkButton={true} pointerEvents={"none"} /> {clearButton(which)} </> : // placeholder image if doc is missing diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1ccf38de2..949e0e168 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -813,7 +813,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); contentScaling = () => this.ContentScale; onClickFunc = () => this.onClickHandler; - setHeight = (height: number) => this.layoutDoc._height = height; + setHeight = (height: number) => { + if (this.props.renderDepth !== -1) { + this.layoutDoc._height = height; + } + } setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); isContentActive = (outsideReaction?: boolean) => { return CurrentUserUtils.SelectedTool !== InkTool.None || diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 37d716993..9956cc36b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -58,8 +58,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps private _sidebarRef = React.createRef<SidebarAnnos>(); private _searchRef = React.createRef<HTMLInputElement>(); private _searchString = ""; + @observable private _webUrl = ""; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render. + @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled @observable private _searching: boolean = false; - @observable _showSidebar = false; + @observable private _showSidebar = false; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt<Doc>; @observable private _marqueeing: number[] | undefined; @@ -79,6 +81,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps super(props); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850); + runInAction(() => this._webUrl = this._url); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it. } @action @@ -239,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale]; - if (word) { + if (word || (e.target as any || "").className.includes("rangeslider") || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) { setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. } else { this._iframeClick = this._iframe ?? undefined; @@ -269,9 +272,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action iframeLoaded = (e: any) => { const iframe = this._iframe; - this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp); + let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString()); + if (requrlraw !== this._url.toString()) { + if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) { + const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g); + const newsearch = matches?.lastElement()!; + if (matches) { + requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch)); + for (let i = 1; i < Array.from(matches)?.length; i++) { + requrlraw = requrlraw.replace(matches[i], ""); + } + } + requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", ""); + } + this.submitURL(requrlraw, undefined, true); + } if (iframe?.contentDocument) { - iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown); + iframe.contentDocument.addEventListener("pointerup", this.iframeUp); + iframe.contentDocument.addEventListener("pointerdown", this.iframeDown); this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight); setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); const initialScroll = this._initialScroll; @@ -281,13 +299,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this._initialScroll = undefined; } iframe.setAttribute("enable-annotation", "true"); - iframe.contentDocument.addEventListener("click", undoBatch(action(e => { + iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; - for (let ele = e.target; ele; ele = ele.parentElement) { + for (let ele = e.target as any; ele; ele = ele.parentElement) { href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href; } - if (href && this.webField?.origin) { - this.submitURL(href.replace(Utils.prepend(""), this.webField?.origin)); + const origin = this.webField?.origin; + if (href && origin) { + e.stopPropagation(); + setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin))); if (this._outerRef.current) { this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop); this._outerRef.current.scrollLeft = 0; @@ -338,8 +358,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []); const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []); if (future.length) { + const curUrl = this._url; this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -350,10 +377,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string")); const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []); if (history.length) { + const curUrl = this._url; if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]); else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); - console.log(this._urlHash); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -362,9 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps static urlHash = (s: string) => { return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0)); } - @action - submitURL = (newUrl?: string, preview?: boolean) => { + submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => { if (!newUrl) return; if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl; try { @@ -376,7 +408,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.layoutDoc._scrollTop = 0; future && (future.length = 0); } - if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); + if (!preview) { + this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); + !dontUpdateIframe && (this._webUrl = this._url); + } } catch (e) { console.log("WebBox URL error:" + this._url); } @@ -430,6 +465,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (!cm.findByDescription("Options...")) { !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ + description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => { + this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts; + if (this._iframe) { + runInAction(() => this._hackHide = true); + setTimeout(action(() => this._hackHide = false)); + } + }, icon: "snowflake" + }); + funcs.push({ description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => { const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); this.layoutDoc.forceReflow = !nw; @@ -468,18 +512,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } @computed get urlContent() { + if (this._hackHide) return (null); const field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />; } else if (field instanceof WebField) { - const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._url) : this._url; + const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl; view = <iframe className="webBox-iframe" enable-annotation={"true"} style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded} // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />; - sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin"} />; + sandbox={`${this.layoutDoc.allowScripts ? "allow-scripts" : ""} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`} />; } else { view = <iframe className="webBox-iframe" enable-annotation={"true"} style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly @@ -615,12 +660,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps moveDocument={this.moveDocument} addDocument={this.sidebarAddDocument} styleProvider={this.childStyleProvider} - pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; + childPointerEvents={this.props.isContentActive() ? "all" : undefined} + pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; return ( <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents() }} > <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}> - <base target="_blank" /> <div className={"webBox-outerContent"} ref={this._outerRef} style={{ width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`, diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 8e61a224c..2a153f256 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -404,7 +404,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) { LightboxView.SetLightboxDoc(undefined); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right + await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right } // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. @@ -1026,6 +1026,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> Array.from(this._selectedArray.keys()).forEach((doc) => doc.presTransition = timeInMS); } + // Converts seconds to ms and updates presTransition + setZoom = (number: String, change?: number) => { + let scale = Number(number) / 100; + if (change) scale += change; + if (scale < 0.01) scale = 0.01; + if (scale > 1.5) scale = 1.5; + Array.from(this._selectedArray.keys()).forEach((doc) => doc.presZoom = scale); + } + // Converts seconds to ms and updates presDuration setDurationTime = (number: String, change?: number) => { let timeInMS = Number(number) * 1000; @@ -1143,11 +1152,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get transitionDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - const type = targetDoc.type; const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection); const isPinWithView: boolean = BoolCast(activeItem.presPinView); if (activeItem && targetDoc) { + const type = targetDoc.type; const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5; + const zoom = activeItem.presZoom ? NumCast(activeItem.presZoom) * 100 : 75; let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2; if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration); const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None'; @@ -1172,6 +1182,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> </div> } + <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}> + <div className="presBox-subheading">Zoom (% screen filled)</div> + <div className="ribbon-property"> + <input className="presBox-input" + type="number" value={zoom} + onChange={action((e) => this.setZoom(e.target.value))} />% + </div> + <div className="ribbon-propertyUpDown"> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}> + <FontAwesomeIcon icon={"caret-up"} /> + </div> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}> + <FontAwesomeIcon icon={"caret-down"} /> + </div> + </div> + </div> + <input type="range" step="1" min="0" max="150" value={zoom} + className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} + id="toolbar-slider" + onPointerDown={() => this._batch = UndoManager.StartBatch("presZoom")} + onPointerUp={() => this._batch?.end()} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + e.stopPropagation(); + this.setZoom(e.target.value); + }} /> <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}> <div className="presBox-subheading">Movement Speed</div> <div className="ribbon-property"> |