UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

155 lines (128 loc) 4.81 kB
import { each } from 'underscore'; import { DeviceUtils, l } from '../../Core'; import { AjaxError } from '../../rest/AjaxError'; import { $$, Dom } from '../../utils/Dom'; export class QuickviewDocumentIframe { public el: HTMLElement; private iframeElement: HTMLIFrameElement; constructor() { this.el = this.buildIFrame().el; } public get iframeHTMLElement() { return this.iframeElement; } public get document() { return this.iframeElement.contentWindow.document; } public get body() { return this.document.body; } public get window() { return this.iframeElement.contentWindow; } public isNewQuickviewDocument(): boolean { const meta = $$(this.document.head).find("meta[name='generator']"); return meta && meta.getAttribute('content') == 'pdf2htmlEX'; } public render(htmlDocument: HTMLDocument, title: string): Promise<HTMLIFrameElement> { if (this.quickviewIsClosedByEndUser()) { return Promise.reject(null); } return new Promise((resolve, reject) => { // Take care to bind the onload function before actually writing to the iframe : // Safari, IE, Edge need this, otherwise the onload function is never called this.iframeElement.onload = () => { resolve(this.iframeElement); }; this.addClientSideTweaksToIFrameStyling(htmlDocument); this.writeToIFrame(htmlDocument); this.iframeElement.title = title; }); } public renderError(error: AjaxError): Promise<HTMLIFrameElement> { if (this.quickviewIsClosedByEndUser()) { return Promise.reject(null); } return new Promise((resolve, reject) => { let errorMessage = ''; switch (error.status) { case 400: errorMessage = l('NoQuickview'); break; default: errorMessage = l('OoopsError'); break; } const errorDocument = document.implementation.createHTMLDocument(); errorDocument.body.style.fontFamily = `Arimo, \'Helvetica Neue\', Helvetica, Arial, sans-serif`; $$(errorDocument.body).text(errorMessage); this.writeToIFrame(errorDocument); resolve(this.iframeElement); }); } private quickviewIsClosedByEndUser() { return this.iframeElement.contentDocument == null; } private buildIFrame(): Dom { const iframe = $$('iframe', { sandbox: 'allow-popups allow-same-origin allow-top-navigation', src: 'about:blank' }); this.iframeElement = iframe.el as HTMLIFrameElement; const iframewrapper = $$('div', { className: 'coveo-iframeWrapper' }); iframewrapper.append(iframe.el); return iframewrapper; } private writeToIFrame(htmlDocument: HTMLDocument) { this.allowDocumentLinkToEscapeSandbox(htmlDocument); this.document.open(); this.document.write(htmlDocument.getElementsByTagName('html')[0].outerHTML); this.document.close(); } private allowDocumentLinkToEscapeSandbox(htmlDocument: HTMLDocument) { // On the iframe, we set the sandbox attribute to "allow top navigation". // For this to work, we need to force all link to target _top. // This is especially useful for quickview on web pages. each($$(htmlDocument.body).findAll('a'), link => { link.setAttribute('target', '_top'); }); } private addClientSideTweaksToIFrameStyling(htmlDocument: HTMLDocument) { const style = $$('style', { type: 'text/css' }).el as HTMLStyleElement; // Safari on iOS forces resize iframes to fit their content, even if an explicit size // is set on the iframe. Isn't that great? By chance there is a trick around it: by // setting a very small size on the body and instead using min-* to set the size to // 100% we're able to trick Safari from thinking it must expand the iframe. Amazed. // The 'scrolling' part is required otherwise the hack doesn't work. // // https://stackoverflow.com/questions/23083462/how-to-get-an-iframe-to-be-responsive-in-ios-safari const cssHackForIOS = ` body, html { height: 1px !important; min-height: 100%; width: 1px !important; min-width: 100%; overflow: scroll; margin: auto } `; const cssText = ` html pre { white-space: pre-wrap; word-wrap: break-word; } body, html { font-family: Arimo, 'Helvetica Neue', Helvetica, Arial, sans-serif; -webkit-text-size-adjust: none; } ${DeviceUtils.isIos() ? cssHackForIOS : ''} `; if (DeviceUtils.isIos()) { $$(this.iframeElement).setAttribute('scrolling', 'no'); this.iframeElement.parentElement.style.margin = '0 0 5px 5px'; } style.appendChild(document.createTextNode(cssText)); htmlDocument.head.appendChild(style); } }