diff options
Diffstat (limited to 'src/client/views/nodes/WebBox.tsx')
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 153 |
1 files changed, 107 insertions, 46 deletions
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 446e83dd3..fc2e4bf61 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { htmlToText } from 'html-to-text'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; @@ -15,7 +17,7 @@ import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, stringHash, Utils } from '../../../Utils'; +import { emptyFunction, stringHash } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; @@ -39,8 +41,9 @@ import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; import { LinkInfo } from './LinkDocPreview'; import { PresBox } from './trails'; import './WebBox.scss'; + const { CreateImage } = require('./WebBoxRenderer'); -const _global = (window /* browser */ || global) /* node */ as any; + @observer export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface { public static LayoutString(fieldKey: string) { @@ -142,19 +145,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem const scrollTop = NumCast(this.layoutDoc._layout_scrollTop); const nativeWidth = NumCast(this.layoutDoc.nativeWidth); const nativeHeight = (nativeWidth * this._props.PanelHeight()) / this._props.PanelWidth(); - var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); + let htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); if (!htmlString) { htmlString = await (await fetch(ClientUtils.CorsProxy(this.webField!.href))).text(); } this.layoutDoc.thumb = undefined; this.Document.thumbLockout = true; // lock to prevent multiple thumb updates. CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop) - .then((data_url: any) => { - if (data_url.includes('<!DOCTYPE')) { + .then((dataUrl: any) => { + if (dataUrl.includes('<!DOCTYPE')) { console.log('BAD DATA IN THUMB CREATION'); return; } - Utils.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename => + ClientUtils.convertDataUri(dataUrl, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename => setTimeout( action(() => { this.Document.thumbLockout = false; @@ -167,7 +170,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem ) ); }) - .catch(function (error: any) { + .catch((error: any) => { console.error('oops, something went wrong!', error); }); }; @@ -188,7 +191,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem }); this._disposers.urlchange = reaction( () => WebCast(this.dataDoc.data), - url => this.submitURL(false, false) + () => this.submitURL(false, false) ); this._disposers.titling = reaction( () => StrCast(this.Document.title), @@ -200,8 +203,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem this._disposers.layout_autoHeight = reaction( () => this.layoutDoc._layout_autoHeight, - layout_autoHeight => { - if (layout_autoHeight) { + layoutAutoHeight => { + if (layoutAutoHeight) { this.layoutDoc._nativeHeight = NumCast(this.Document[this._props.fieldKey + '_nativeHeight']); this._props.setHeight?.(NumCast(this.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1)); } @@ -221,7 +224,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem } // else it's an HTMLfield } else if (this.webField && !this.dataDoc.text) { WebRequest.get(ClientUtils.CorsProxy(this.webField.href)) // - .then(result => result && (this.dataDoc.text = htmlToText(result.content))); + .then(result => { + result && (this.dataDoc.text = htmlToText(result.content)); + }); } this._disposers.scrollReaction = reaction( @@ -297,21 +302,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem const focusTime = options.zoomTime ?? 500; this.goTo(scrollTo, focusTime, options.easeFunc); return focusTime; - } else { - this._initialScroll = scrollTo; } + this._initialScroll = scrollTo; } } + return undefined; }; @action - getView = (doc: Doc, options: FocusViewOptions) => { - if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.DocumentView?.())); + getView = (doc: Doc /* , options: FocusViewOptions */) => { + if (Doc.AreProtosEqual(doc, this.Document)) + return new Promise<Opt<DocumentView>>(res => { + res(this.DocumentView?.()); + }); if (this.Document.layout_fieldKey === 'layout_icon') this.DocumentView?.().iconify(); const webUrl = WebCast(doc.config_data)?.url; if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href); if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); - return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + return new Promise<Opt<DocumentView>>(res => { + DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + }); }; sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { @@ -322,14 +332,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem return this._props.addDocTab(doc, where); }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { - let ele: Opt<HTMLDivElement> = undefined; + let ele: Opt<HTMLDivElement>; try { const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents(); if (contents) { ele = document.createElement('div'); ele.append(contents); } - } catch (e) {} + } catch (e) { + /* empty */ + } const visibleAnchor = this._getAnchor(this._savedAnnotations, true); const anchor = visibleAnchor ?? @@ -338,7 +350,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem y: NumCast(this.layoutDoc._layout_scrollTop), annotationOn: this.Document, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.Document); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: !!pinProps?.pinData, pannable: true } }, this.Document); anchor.text = ele?.textContent ?? ''; anchor.text_html = ele?.innerHTML ?? this._selectionText; addAsAnnotation && this.addDocumentWrapper(anchor); @@ -395,7 +407,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem e?.stopPropagation(); setTimeout(() => { // if menu comes up right away, the down event can still be active causing a menu item to be selected - this.specificContextMenu(undefined as any); + this.specificContextMenu(); this.DocumentView?.().onContextMenu(undefined, theclick[0], theclick[1]); }); } @@ -470,6 +482,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem const sheets = document.head.appendChild(style); return (sheets as any).sheet; } + return undefined; } addWebStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') { const propText = @@ -484,7 +497,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem _iframetimeout: any = undefined; @observable _warning = 0; @action - iframeLoaded = (e: any) => { + iframeLoaded = () => { const iframe = this._iframe; if (this._initialScroll !== undefined) { this.setScrollPos(this._initialScroll); @@ -640,12 +653,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem this._scrollHeight = 0; if (this._webUrl === this._url) { this._webUrl = curUrl; - setTimeout(action(() => (this._webUrl = this._url))); + setTimeout( + action(() => { + this._webUrl = this._url; + }) + ); } else { this._webUrl = this._url; } return true; } + return undefined; }); return false; }; @@ -663,12 +681,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem this._scrollHeight = 0; if (this._webUrl === this._url) { this._webUrl = curUrl; - setTimeout(action(() => (this._webUrl = this._url))); + setTimeout( + action(() => { + this._webUrl = this._url; + }) + ); } else { this._webUrl = this._url; } return true; } + return undefined; }); return false; }; @@ -700,7 +723,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem const html = dataTransfer.getData('text/html'); const uri = dataTransfer.getData('text/uri-list'); const url = uri || html || this._url || ''; - const newurl = url.startsWith(window.location.origin) ? url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || '') : url; + const newurl = url.startsWith(window.location.origin) ? url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^/]*/)?.[0] || '') : url; this.setData(newurl); e.stopPropagation(); }; @@ -723,19 +746,31 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem e.stopPropagation(); }; - specificContextMenu = (e: React.MouseEvent | PointerEvent): void => { + specificContextMenu = (): void => { const cm = ContextMenu.Instance; const funcs: ContextMenuProps[] = []; if (!cm.findByDescription('Options...')) { !Doc.noviceMode && - funcs.push({ description: (this.layoutDoc[this.fieldKey + '_useCors'] ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc[this.fieldKey + '_useCors'] = !this.layoutDoc[this.fieldKey + '_useCors']), icon: 'snowflake' }); + funcs.push({ + description: (this.layoutDoc[this.fieldKey + '_useCors'] ? "Don't Use" : 'Use') + ' Cors', + event: () => { + this.layoutDoc[this.fieldKey + '_useCors'] = !this.layoutDoc[this.fieldKey + '_useCors']; + }, + icon: 'snowflake', + }); funcs.push({ description: (this.dataDoc[this.fieldKey + '_allowScripts'] ? 'Prevent' : 'Allow') + ' Scripts', event: () => { this.dataDoc[this.fieldKey + '_allowScripts'] = !this.dataDoc[this.fieldKey + '_allowScripts']; if (this._iframe) { - runInAction(() => (this._hackHide = true)); - setTimeout(action(() => (this._hackHide = false))); + runInAction(() => { + this._hackHide = true; + }); + setTimeout( + action(() => { + this._hackHide = false; + }) + ); } }, icon: 'snowflake', @@ -773,7 +808,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem setupMoveUpEvents( this, e, - action(e => { + action(() => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); return true; }), @@ -797,7 +832,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem @observable lighttext = false; @computed get urlContent() { - if (this.ScreenToLocalBoxXf().Scale > 25) return <div></div>; + if (this.ScreenToLocalBoxXf().Scale > 25) return <div />; setTimeout( action(() => { if (this._initialScroll === undefined && !this._webPageHasBeenRendered) { @@ -826,12 +861,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem if (field instanceof WebField) { const url = this.layoutDoc[this.fieldKey + '_useCors'] ? ClientUtils.CorsProxy(this._webUrl) : this._webUrl; const scripts = this.dataDoc[this.fieldKey + '_allowScripts'] || this._webUrl.includes('wikipedia.org') || this._webUrl.includes('google.com') || this._webUrl.startsWith('https://bing'); - //if (!scripts) console.log('No scripts for: ' + url); + // if (!scripts) console.log('No scripts for: ' + url); return ( <iframe + title="web iframe" key={this._warning} className="webBox-iframe" - ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))} + ref={action((r: HTMLIFrameElement | null) => { + this._iframe = r; + })} style={{ pointerEvents: SnappingManager.IsResizing ? 'none' : undefined }} src={url} onLoad={this.iframeLoaded} @@ -842,11 +880,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem /> ); } - return <iframe className="webBox-iframe" ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))} src={'https://crossorigin.me/https://cs.brown.edu'} />; + return ( + <iframe + title="web frame" + className="webBox-iframe" + ref={action((r: HTMLIFrameElement | null) => { + this._iframe = r; + })} + src="https://crossorigin.me/https://cs.brown.edu" + /> + ); } addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { - this._url && (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url))); + this._url && + (doc instanceof Doc ? [doc] : doc).forEach(doc => { + doc.config_data = new WebField(this._url); + }); return this.addDocument(doc, annotationKey); }; @@ -900,14 +950,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, }} onPointerDown={e => this.sidebarBtnDown(e, true)}> - <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" /> + <FontAwesomeIcon style={{ color: Colors.WHITE }} icon="comment-alt" size="sm" /> </div> ); } @observable _previewNativeWidth: Opt<number> = undefined; @observable _previewWidth: Opt<number> = undefined; toggleSidebar = action((preview: boolean = false) => { - var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); + let nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']); if (!nativeWidth) { const defaultNativeWidth = NumCast(this.Document.nativeWidth, this.dataDoc[this.fieldKey] instanceof WebField ? 850 : NumCast(this.Document._width)); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth); @@ -946,7 +996,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem }; _innerCollectionView: CollectionFreeFormView | undefined; zoomScaling = () => this._innerCollectionView?.zoomScaling() ?? 1; - setInnerContent = (component: ViewBoxInterface) => (this._innerCollectionView = component as CollectionFreeFormView); + setInnerContent = (component: ViewBoxInterface) => { + this._innerCollectionView = component as CollectionFreeFormView; + }; @computed get content() { const interactive = this._props.isContentActive() && this._props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None; @@ -977,24 +1029,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem {this.inlineTextAnnotations .sort((a, b) => NumCast(a.y) - NumCast(b.y)) .map(anno => ( + // eslint-disable-next-line react/jsx-props-no-spreading <Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} /> ))} </div> ); } @computed get SidebarShown() { - return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false; + return !!(this._showSidebar || this.layoutDoc._layout_showSidebar); } renderAnnotations = (childFilters: () => string[]) => ( <CollectionFreeFormView + // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} setContentViewBox={this.setInnerContent} NativeWidth={returnZero} NativeHeight={returnZero} originTopLeft={false} - isAnnotationOverlayScrollable={true} + isAnnotationOverlayScrollable renderDepth={this._props.renderDepth + 1} - isAnnotationOverlay={true} + isAnnotationOverlay fieldKey={this.annotationKey} setPreviewCursor={this.setPreviewCursor} PanelWidth={this.panelWidth} @@ -1037,7 +1091,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem }} // when active, block wheel events from propagating since they're handled by the iframe onWheel={this.onZoomWheel} - onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} + onScroll={() => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} onPointerDown={this.onMarqueeDown}> <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this._props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}> {this.content} @@ -1053,7 +1107,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem return ( <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}> <div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> - <button className="webBox-overlayButton" title={'search'} /> + <button type="button" className="webBox-overlayButton" title="search" /> <input className="webBox-searchBar" placeholder="Search" @@ -1064,13 +1118,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem e.stopPropagation(); }} /> - <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}> + <button type="button" className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}> <FontAwesomeIcon icon="search" size="sm" /> </button> </div> <button + type="button" className="webBox-overlayButton" - title={'search'} + title="search" onClick={action(() => { this._searching = !this._searching; this.search('', false, true); @@ -1083,8 +1138,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem </div> ); } - searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value); - setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func); + searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => { + this._searchString = e.currentTarget.value; + }; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => { + this._setPreviewCursor = func; + }; panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); scrollXf = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); @@ -1157,6 +1216,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}> <SidebarAnnos ref={this._sidebarRef} + // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} fieldKey={this.fieldKey + '_' + this._urlHash} @@ -1177,6 +1237,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem ); } } +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function urlHash(url: string) { return stringHash(url); }); |