diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 23 | ||||
| -rw-r--r-- | src/client/views/nodes/FilterBox.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 136 |
4 files changed, 93 insertions, 78 deletions
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index abb01c466..49cd39f43 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -87,7 +87,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl return whichDoc ? <> <DocumentView ref={(r) => { - whichDoc !== targetDoc && r?.focus(targetDoc); + whichDoc !== targetDoc && r?.focus(whichDoc); }} {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} isContentActive={returnFalse} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e117be0c2..f30d093a1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -168,6 +168,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { radialMenu?: String[]; LayoutTemplateString?: string; dontCenter?: "x" | "y" | "xy"; + dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NativeWidth?: () => number; NativeHeight?: () => number; @@ -249,6 +250,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); } } + @action cleanupHandlers(unbrush: boolean) { this._dropDisposer?.(); this._multiTouchDisposer?.(); @@ -856,9 +858,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this.props.isContentActive()) ? true : undefined; } @observable _retryThumb = 1; - thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb && - !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) && - !this._componentView?.isAnyChildContentActive?.() ? true : false + thumbShown = () => { + return !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb && + !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) && + !Doc.isBrushedHighlightedDegree(this.props.Document) && + !this._componentView?.isAnyChildContentActive?.() ? true : false; + } @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -874,7 +879,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined, }}> {!this._retryThumb || !this.thumbShown() ? (null) : - <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()} + <img style={{ background: "white", top: 0, position: "relative" }} src={this.thumb} // + '?d=' + (new Date()).getTime()} width={this.props.PanelWidth()} height={this.props.PanelHeight()} onError={(e: any) => { setTimeout(action(() => this._retryThumb = 0), 0); @@ -1109,8 +1114,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps render() { TraceMobx(); const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString - const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange"][highlightIndex]; - const highlightStyle = ["solid", "dashed", "solid", "solid"][highlightIndex]; + const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex]; + const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex]; const excludeTypes = !this.props.treeViewDoc && highlightIndex < 3 ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON]; let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear; highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]"; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way @@ -1126,8 +1131,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps onKeyDown={this.onKeyDown} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)} - onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)} + onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))} + onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))} style={{ display: this.hidden ? "inline" : undefined, borderRadius: this.borderRounding, @@ -1207,7 +1212,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { } @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || - this.props.ContainingCollectionView?.collectionViewType === CollectionViewType.Time || + this.props.dontScaleFilter?.(this.Document) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any); } @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); } diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index dc0ab5ec2..78adf63cb 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -96,17 +96,15 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() { runInAction(() => this._loaded = true); }, { fireImmediately: true }); } - @observable _allDocs = [] as Doc[]; + @computed get allDocs() { // trace(); + const allDocs = new Set<Doc>(); const targetDoc = FilterBox.targetDoc; if (this._loaded && targetDoc) { - const allDocs = new Set<Doc>(); - const activeTabs = FilterBox.targetDocChildren; - SearchBox.foreachRecursiveDoc(activeTabs, (depth, doc) => allDocs.add(doc)); - setTimeout(action(() => this._allDocs = Array.from(allDocs))); + SearchBox.foreachRecursiveDoc(FilterBox.targetDocChildren, (depth, doc) => allDocs.add(doc)); } - return this._allDocs; + return Array.from(allDocs); } @computed get _allFacets() { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 64655ba0b..af7a2b941 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -40,7 +40,6 @@ import React = require("react"); const { CreateImage } = require("./WebBoxRenderer"); const _global = (window /* browser */ || global /* node */) as any; const htmlToText = require("html-to-text"); - @observer export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } @@ -52,7 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps private _disposers: { [name: string]: IReactionDisposer } = {}; private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _keyInput = React.createRef<HTMLInputElement>(); - private _initialScroll: Opt<number>; + private _initialScroll: Opt<number> = NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)); private _sidebarRef = React.createRef<SidebarAnnos>(); private _searchRef = React.createRef<HTMLInputElement>(); private _searchString = ""; @@ -68,14 +67,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable private _iframeClick: HTMLIFrameElement | undefined = undefined; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); - @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight, 1500); + @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight); @computed get _url() { return this.webField?.toString() || ""; } @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; } - @computed get scrollHeight() { return this._scrollHeight; } + @computed get scrollHeight() { return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight); } @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => 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; } + @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url } constructor(props: any) { super(props); @@ -100,14 +99,49 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } return true; } + @action setScrollPos = (pos: number) => { if (!this._outerRef.current || this._outerRef.current.scrollHeight < pos) { - setTimeout(() => this.setScrollPos(pos), 250); + if (this._webPageHasBeenRendered) setTimeout(() => this.setScrollPos(pos), 250); } else { this._outerRef.current.scrollTop = pos; this._initialScroll = undefined; } } + + lockout = false; + updateThumb = async () => { + const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href; + const scrollTop = NumCast(this.layoutDoc._scrollTop); + if (!this.lockout && this._iframe && !imageBitmap && scrollTop !== this.layoutDoc.thumbScrollTop) { + var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument); + if (!htmlString) { + htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text(); + } + this.layoutDoc.thumb = undefined; + this.lockout = true; // lock to prevent multiple thumb updates. + 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(), + scrollTop + ).then + ((data_url: any) => { + VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then( + returnedfilename => setTimeout(action(() => { + this.lockout = false; + this.layoutDoc.thumb = new ImageField(returnedfilename); + this.layoutDoc.thumbScrollTop = scrollTop + }), 500)); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + } + } async componentDidMount() { this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons) @@ -117,45 +151,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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"`); }); - reaction(() => this.props.isSelected(), + reaction(() => this.props.isSelected() || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), async (selected) => { if (selected) { this._webPageHasBeenRendered = true; - setTimeout(action(() => { - this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0); - if (this._initialScroll !== undefined) { - this.setScrollPos(this._initialScroll); - } - })); } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail) !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty. - 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(); - } - this.layoutDoc.thumb = undefined; - 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 - ((data_url: any) => { - VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then( - returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500)); - }) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); - }); - } + this.updateThumb(); } - }, { fireImmediately: this.props.isSelected() }); + }, { fireImmediately: this.props.isSelected() || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegree(this.props.Document) ? true : false) }); this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight, autoHeight => { @@ -183,21 +188,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } } - var quickScroll = true; this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop), (scrollTop) => { - if (quickScroll) this._initialScroll = scrollTop; - else { - const viewTrans = StrCast(this.Document._viewTransition); - const durationMiliStr = viewTrans.match(/([0-9]*)ms/); - const durationSecStr = viewTrans.match(/([0-9.]*)s/); - const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0; - this.goTo(scrollTop, duration); - } + const viewTrans = StrCast(this.Document._viewTransition); + const durationMiliStr = viewTrans.match(/([0-9]*)ms/); + const durationSecStr = viewTrans.match(/([0-9.]*)s/); + const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0; + this.goTo(scrollTop, duration); }, { fireImmediately: true } ); - quickScroll = false; } @action componentWillUnmount() { Object.values(this._disposers).forEach(disposer => disposer?.()); @@ -234,7 +234,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { - this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth); if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { this.toggleSidebar(!smooth); @@ -242,15 +241,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; if (doc !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); - const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1, this.getScrollHeight()); - if (scrollTo !== undefined) { + const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1, + Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight())); + if (scrollTo !== undefined && this._initialScroll === undefined) { const focusSpeed = smooth ? 500 : 0; - this._initialScroll !== undefined && (this._initialScroll = scrollTo); this.goTo(scrollTo, focusSpeed); return focusSpeed; + } else if (!this._webPageHasBeenRendered || !this.getScrollHeight() || this._initialScroll !== undefined) { + this._initialScroll = scrollTo; } } - this._initialScroll = NumCast(this.layoutDoc._scrollTop); return undefined; } @@ -324,6 +324,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action iframeLoaded = (e: any) => { const iframe = this._iframe; + if (this._initialScroll !== undefined) { + this.setScrollPos(this._initialScroll); + } 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) { @@ -342,8 +345,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (iframe?.contentDocument) { 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); + this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument.body.scrollHeight); + setTimeout(action(() => this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); iframe.setAttribute("enable-annotation", "true"); iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; @@ -374,29 +377,29 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action setDashScrollTop = (scrollTop: number, timeout: number = 250) => { - const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight()); - timeout = scrollTop > iframeHeight ? 0 : timeout; + const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight()); this._scrollTimer && clearTimeout(this._scrollTimer); this._scrollTimer = setTimeout(action(() => { this._scrollTimer = undefined; if (!LinkDocPreview.LinkInfo && this._outerRef.current && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) { - this.layoutDoc._scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop; + this.layoutDoc.thumb = undefined; + this.layoutDoc.thumbScrollTop = undefined; + this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop; } }), timeout); } goTo = (scrollTop: number, duration: number) => { if (this._outerRef.current) { - const iframeHeight = Math.max(1000, this._scrollHeight - this.panelHeight()); - scrollTop = scrollTop > iframeHeight + 50 ? iframeHeight : scrollTop; + const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight()); if (duration) { smoothScroll(duration, [this._outerRef.current], scrollTop); this.setDashScrollTop(scrollTop, duration); } else { this.setDashScrollTop(scrollTop); } - } + } else this._initialScroll = scrollTop; } forward = (checkAvailable?: boolean) => { @@ -456,6 +459,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (url && !preview) { this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]); this.layoutDoc._scrollTop = 0; + if (this._webPageHasBeenRendered) { + this.layoutDoc.thumb = undefined; + this.layoutDoc.thumbScrollTop = undefined; + } future && (future.length = 0); } if (!preview) { @@ -566,6 +573,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @computed get urlContent() { if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null); + this.props.thumbShown?.(); const field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { @@ -583,7 +591,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />; } - setTimeout(action(() => this._webPageHasBeenRendered = true)); + setTimeout(action(() => { + this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0); + if (this._initialScroll === undefined && !this._webPageHasBeenRendered) this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop))); + this._webPageHasBeenRendered = true; + })); return view; } |
