UNPKG

5.75 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { ServerConnection } from '@jupyterlab/services';
4/**
5 * Any object is "printable" if it implements the `IPrintable` interface.
6 *
7 * To do this it, it must have a method called `Printing.symbol` which returns either a function
8 * to print the object or null if it cannot be printed.
9 *
10 * One way of printing is to use the `printWidget` function, which creates a hidden iframe
11 * and copies the DOM nodes from your widget to that iframe and printing just that iframe.
12 *
13 * Another way to print is to use the `printURL` function, which takes a URL and prints that page.
14 */
15export var Printing;
16(function (Printing) {
17 /**
18 * Symbol to use for a method that returns a function to print an object.
19 */
20 Printing.symbol = Symbol('printable');
21 /**
22 * Returns whether an object implements a print method.
23 */
24 function isPrintable(a) {
25 if (typeof a !== 'object' || !a) {
26 return false;
27 }
28 return Printing.symbol in a;
29 }
30 Printing.isPrintable = isPrintable;
31 /**
32 * Returns the print function for an object, or null if it does not provide a handler.
33 */
34 function getPrintFunction(val) {
35 if (isPrintable(val)) {
36 return val[Printing.symbol]();
37 }
38 return null;
39 }
40 Printing.getPrintFunction = getPrintFunction;
41 /**
42 * Prints a widget by copying it's DOM node
43 * to a hidden iframe and printing that iframe.
44 */
45 function printWidget(widget) {
46 return printContent(widget.node);
47 }
48 Printing.printWidget = printWidget;
49 /**
50 * Prints a URL by loading it into an iframe.
51 *
52 * @param url URL to load into an iframe.
53 */
54 async function printURL(url) {
55 const settings = ServerConnection.makeSettings();
56 const text = await (await ServerConnection.makeRequest(url, {}, settings)).text();
57 return printContent(text);
58 }
59 Printing.printURL = printURL;
60 /**
61 * Prints a URL or an element in an iframe and then removes the iframe after printing.
62 */
63 async function printContent(textOrEl) {
64 const isText = typeof textOrEl === 'string';
65 const iframe = createIFrame();
66 const parent = window.document.body;
67 parent.appendChild(iframe);
68 if (isText) {
69 iframe.srcdoc = textOrEl;
70 await resolveWhenLoaded(iframe);
71 }
72 else {
73 iframe.src = 'about:blank';
74 await resolveWhenLoaded(iframe);
75 setIFrameNode(iframe, textOrEl);
76 }
77 const printed = resolveAfterEvent();
78 launchPrint(iframe.contentWindow);
79 // Once the print dialog has been dismissed, we regain event handling,
80 // and it should be safe to discard the hidden iframe.
81 await printed;
82 parent.removeChild(iframe);
83 }
84 /**
85 * Creates a new hidden iframe and appends it to the document
86 *
87 * Modified from
88 * https://github.com/joseluisq/printd/blob/eb7948d602583c055ab6dee3ee294b6a421da4b6/src/index.ts#L24
89 */
90 function createIFrame() {
91 const el = window.document.createElement('iframe');
92 // We need both allow-modals and allow-same-origin to be able to
93 // call print in the iframe.
94 // We intentionally do not allow scripts:
95 // https://github.com/jupyterlab/jupyterlab/pull/5850#pullrequestreview-230899790
96 el.setAttribute('sandbox', 'allow-modals allow-same-origin');
97 const css = 'visibility:hidden;width:0;height:0;position:absolute;z-index:-9999;bottom:0;';
98 el.setAttribute('style', css);
99 el.setAttribute('width', '0');
100 el.setAttribute('height', '0');
101 return el;
102 }
103 /**
104 * Copies a node from the base document to the iframe.
105 */
106 function setIFrameNode(iframe, node) {
107 iframe.contentDocument.body.appendChild(node.cloneNode(true));
108 iframe.contentDocument.close();
109 }
110 /**
111 * Promise that resolves when all resources are loaded in the window.
112 */
113 function resolveWhenLoaded(iframe) {
114 return new Promise(resolve => {
115 iframe.onload = () => resolve();
116 });
117 }
118 /**
119 * A promise that resolves after the next mousedown, mousemove, or
120 * keydown event. We use this as a proxy for determining when the
121 * main window has regained control after the print dialog is removed.
122 *
123 * We can't use the usual window.onafterprint handler because we
124 * disallow Javascript execution in the print iframe.
125 */
126 function resolveAfterEvent() {
127 return new Promise(resolve => {
128 const onEvent = () => {
129 document.removeEventListener('mousemove', onEvent, true);
130 document.removeEventListener('mousedown', onEvent, true);
131 document.removeEventListener('keydown', onEvent, true);
132 resolve();
133 };
134 document.addEventListener('mousemove', onEvent, true);
135 document.addEventListener('mousedown', onEvent, true);
136 document.addEventListener('keydown', onEvent, true);
137 });
138 }
139 /**
140 * Prints a content window.
141 */
142 function launchPrint(contentWindow) {
143 const result = contentWindow.document.execCommand('print', false);
144 // execCommand won't work in firefox so we call the `print` method instead if it fails
145 // https://github.com/joseluisq/printd/blob/eb7948d602583c055ab6dee3ee294b6a421da4b6/src/index.ts#L148
146 if (!result) {
147 contentWindow.print();
148 }
149 }
150})(Printing || (Printing = {}));
151//# sourceMappingURL=printing.js.map
\No newline at end of file