1 | // Copyright (c) Jupyter Development Team.
|
2 | // Distributed under the terms of the Modified BSD License.
|
3 | import { 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 | */
|
15 | export 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 |