1 |
|
2 |
|
3 |
|
4 | import { ServerConnection } from '@jupyterlab/services';
|
5 | import { Widget } from '@lumino/widgets';
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | export namespace Printing {
|
20 | |
21 |
|
22 |
|
23 | export type OptionalAsyncThunk = (() => Promise<void>) | null;
|
24 |
|
25 | /**
|
26 | * Symbol to use for a method that returns a function to print an object.
|
27 | */
|
28 | export const symbol = Symbol('printable');
|
29 |
|
30 | /**
|
31 | * Objects who provide a custom way of printing themselves
|
32 | * should implement this interface.
|
33 | */
|
34 | export interface IPrintable {
|
35 | /**
|
36 | * Returns a function to print this object or null if it cannot be printed.
|
37 | */
|
38 | [symbol]: () => OptionalAsyncThunk;
|
39 | }
|
40 |
|
41 | |
42 |
|
43 |
|
44 | export function isPrintable(a: unknown): a is IPrintable {
|
45 | if (typeof a !== 'object' || !a) {
|
46 | return false;
|
47 | }
|
48 | return symbol in a;
|
49 | }
|
50 |
|
51 | |
52 |
|
53 |
|
54 | export function getPrintFunction(val: unknown): OptionalAsyncThunk {
|
55 | if (isPrintable(val)) {
|
56 | return val[symbol]();
|
57 | }
|
58 | return null;
|
59 | }
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 | export function printWidget(widget: Widget): Promise<void> {
|
66 | return printContent(widget.node);
|
67 | }
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 | export async function printURL(url: string): Promise<void> {
|
75 | const settings = ServerConnection.makeSettings();
|
76 | const text = await (
|
77 | await ServerConnection.makeRequest(url, {}, settings)
|
78 | ).text();
|
79 | return printContent(text);
|
80 | }
|
81 |
|
82 | |
83 |
|
84 |
|
85 | async function printContent(textOrEl: string | HTMLElement): Promise<void> {
|
86 | const isText = typeof textOrEl === 'string';
|
87 | const iframe = createIFrame();
|
88 |
|
89 | const parent = window.document.body;
|
90 | parent.appendChild(iframe);
|
91 | if (isText) {
|
92 | iframe.srcdoc = textOrEl as string;
|
93 | await resolveWhenLoaded(iframe);
|
94 | } else {
|
95 | iframe.src = 'about:blank';
|
96 | await resolveWhenLoaded(iframe);
|
97 | setIFrameNode(iframe, textOrEl as HTMLElement);
|
98 | }
|
99 | const printed = resolveAfterEvent();
|
100 | launchPrint(iframe.contentWindow!);
|
101 |
|
102 |
|
103 | await printed;
|
104 | parent.removeChild(iframe);
|
105 | }
|
106 |
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | function createIFrame(): HTMLIFrameElement {
|
114 | const el = window.document.createElement('iframe');
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | el.setAttribute('sandbox', 'allow-modals allow-same-origin');
|
121 | const css =
|
122 | 'visibility:hidden;width:0;height:0;position:absolute;z-index:-9999;bottom:0;';
|
123 | el.setAttribute('style', css);
|
124 | el.setAttribute('width', '0');
|
125 | el.setAttribute('height', '0');
|
126 |
|
127 | return el;
|
128 | }
|
129 |
|
130 | |
131 |
|
132 |
|
133 | function setIFrameNode(iframe: HTMLIFrameElement, node: HTMLElement) {
|
134 | iframe.contentDocument!.body.appendChild(node.cloneNode(true));
|
135 | iframe.contentDocument!.close();
|
136 | }
|
137 |
|
138 | |
139 |
|
140 |
|
141 | function resolveWhenLoaded(iframe: HTMLIFrameElement): Promise<void> {
|
142 | return new Promise(resolve => {
|
143 | iframe.onload = () => resolve();
|
144 | });
|
145 | }
|
146 |
|
147 | |
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | function resolveAfterEvent(): Promise<void> {
|
156 | return new Promise(resolve => {
|
157 | const onEvent = () => {
|
158 | document.removeEventListener('mousemove', onEvent, true);
|
159 | document.removeEventListener('mousedown', onEvent, true);
|
160 | document.removeEventListener('keydown', onEvent, true);
|
161 | resolve();
|
162 | };
|
163 | document.addEventListener('mousemove', onEvent, true);
|
164 | document.addEventListener('mousedown', onEvent, true);
|
165 | document.addEventListener('keydown', onEvent, true);
|
166 | });
|
167 | }
|
168 |
|
169 | |
170 |
|
171 |
|
172 | function launchPrint(contentWindow: Window) {
|
173 | const result = contentWindow.document.execCommand('print', false);
|
174 |
|
175 |
|
176 | if (!result) {
|
177 | contentWindow.print();
|
178 | }
|
179 | }
|
180 | }
|