@jupyterlab/apputils
Version:
JupyterLab - Application Utilities
151 lines • 5.75 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { ServerConnection } from '@jupyterlab/services';
/**
* Any object is "printable" if it implements the `IPrintable` interface.
*
* To do this it, it must have a method called `Printing.symbol` which returns either a function
* to print the object or null if it cannot be printed.
*
* One way of printing is to use the `printWidget` function, which creates a hidden iframe
* and copies the DOM nodes from your widget to that iframe and printing just that iframe.
*
* Another way to print is to use the `printURL` function, which takes a URL and prints that page.
*/
export var Printing;
(function (Printing) {
/**
* Symbol to use for a method that returns a function to print an object.
*/
Printing.symbol = Symbol('printable');
/**
* Returns whether an object implements a print method.
*/
function isPrintable(a) {
if (typeof a !== 'object' || !a) {
return false;
}
return Printing.symbol in a;
}
Printing.isPrintable = isPrintable;
/**
* Returns the print function for an object, or null if it does not provide a handler.
*/
function getPrintFunction(val) {
if (isPrintable(val)) {
return val[Printing.symbol]();
}
return null;
}
Printing.getPrintFunction = getPrintFunction;
/**
* Prints a widget by copying it's DOM node
* to a hidden iframe and printing that iframe.
*/
function printWidget(widget) {
return printContent(widget.node);
}
Printing.printWidget = printWidget;
/**
* Prints a URL by loading it into an iframe.
*
* @param url URL to load into an iframe.
*/
async function printURL(url) {
const settings = ServerConnection.makeSettings();
const text = await (await ServerConnection.makeRequest(url, {}, settings)).text();
return printContent(text);
}
Printing.printURL = printURL;
/**
* Prints a URL or an element in an iframe and then removes the iframe after printing.
*/
async function printContent(textOrEl) {
const isText = typeof textOrEl === 'string';
const iframe = createIFrame();
const parent = window.document.body;
parent.appendChild(iframe);
if (isText) {
iframe.srcdoc = textOrEl;
await resolveWhenLoaded(iframe);
}
else {
iframe.src = 'about:blank';
await resolveWhenLoaded(iframe);
setIFrameNode(iframe, textOrEl);
}
const printed = resolveAfterEvent();
launchPrint(iframe.contentWindow);
// Once the print dialog has been dismissed, we regain event handling,
// and it should be safe to discard the hidden iframe.
await printed;
parent.removeChild(iframe);
}
/**
* Creates a new hidden iframe and appends it to the document
*
* Modified from
* https://github.com/joseluisq/printd/blob/eb7948d602583c055ab6dee3ee294b6a421da4b6/src/index.ts#L24
*/
function createIFrame() {
const el = window.document.createElement('iframe');
// We need both allow-modals and allow-same-origin to be able to
// call print in the iframe.
// We intentionally do not allow scripts:
// https://github.com/jupyterlab/jupyterlab/pull/5850#pullrequestreview-230899790
el.setAttribute('sandbox', 'allow-modals allow-same-origin');
const css = 'visibility:hidden;width:0;height:0;position:absolute;z-index:-9999;bottom:0;';
el.setAttribute('style', css);
el.setAttribute('width', '0');
el.setAttribute('height', '0');
return el;
}
/**
* Copies a node from the base document to the iframe.
*/
function setIFrameNode(iframe, node) {
iframe.contentDocument.body.appendChild(node.cloneNode(true));
iframe.contentDocument.close();
}
/**
* Promise that resolves when all resources are loaded in the window.
*/
function resolveWhenLoaded(iframe) {
return new Promise(resolve => {
iframe.onload = () => resolve();
});
}
/**
* A promise that resolves after the next mousedown, mousemove, or
* keydown event. We use this as a proxy for determining when the
* main window has regained control after the print dialog is removed.
*
* We can't use the usual window.onafterprint handler because we
* disallow Javascript execution in the print iframe.
*/
function resolveAfterEvent() {
return new Promise(resolve => {
const onEvent = () => {
document.removeEventListener('mousemove', onEvent, true);
document.removeEventListener('mousedown', onEvent, true);
document.removeEventListener('keydown', onEvent, true);
resolve();
};
document.addEventListener('mousemove', onEvent, true);
document.addEventListener('mousedown', onEvent, true);
document.addEventListener('keydown', onEvent, true);
});
}
/**
* Prints a content window.
*/
function launchPrint(contentWindow) {
const result = contentWindow.document.execCommand('print', false);
// execCommand won't work in firefox so we call the `print` method instead if it fails
// https://github.com/joseluisq/printd/blob/eb7948d602583c055ab6dee3ee294b6a421da4b6/src/index.ts#L148
if (!result) {
contentWindow.print();
}
}
})(Printing || (Printing = {}));
//# sourceMappingURL=printing.js.map