@jupyterlab/pdf-extension 4.0.0-alpha.8 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyterlab/pdf-extension",
3
- "version": "4.0.0-alpha.8",
3
+ "version": "4.0.0-beta.0",
4
4
  "description": "JupyterLab - PDF Viewer",
5
5
  "homepage": "https://github.com/jupyterlab/jupyterlab",
6
6
  "bugs": {
@@ -27,7 +27,8 @@
27
27
  "lib/*.js.map",
28
28
  "lib/*.js",
29
29
  "style/*.css",
30
- "style/index.js"
30
+ "style/index.js",
31
+ "src/**/*.{ts,tsx}"
31
32
  ],
32
33
  "scripts": {
33
34
  "build": "tsc -b",
@@ -36,15 +37,15 @@
36
37
  "watch": "tsc -b --watch"
37
38
  },
38
39
  "dependencies": {
39
- "@jupyterlab/rendermime-interfaces": "^4.0.0-alpha.8",
40
- "@lumino/coreutils": "^1.12.0",
41
- "@lumino/disposable": "^1.10.1",
42
- "@lumino/widgets": "^1.31.1"
40
+ "@jupyterlab/rendermime-interfaces": "^3.8.0-beta.0",
41
+ "@lumino/coreutils": "^2.0.0",
42
+ "@lumino/disposable": "^2.0.0",
43
+ "@lumino/widgets": "^2.0.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "rimraf": "~3.0.0",
46
- "typedoc": "~0.22.10",
47
- "typescript": "~4.5.2"
47
+ "typedoc": "~0.23.25",
48
+ "typescript": "~5.0.2"
48
49
  },
49
50
  "publishConfig": {
50
51
  "access": "public"
package/src/index.ts ADDED
@@ -0,0 +1,198 @@
1
+ // Copyright (c) Jupyter Development Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+ /**
4
+ * @packageDocumentation
5
+ * @module pdf-extension
6
+ */
7
+
8
+ import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
9
+ import { PromiseDelegate } from '@lumino/coreutils';
10
+ import { DisposableDelegate } from '@lumino/disposable';
11
+ import { Widget } from '@lumino/widgets';
12
+
13
+ /**
14
+ * The MIME type for PDF.
15
+ */
16
+ const MIME_TYPE = 'application/pdf';
17
+
18
+ /**
19
+ * A class for rendering a PDF document.
20
+ */
21
+ export class RenderedPDF extends Widget implements IRenderMime.IRenderer {
22
+ constructor() {
23
+ super();
24
+ this.addClass('jp-PDFContainer');
25
+ // We put the object in an iframe, which seems to have a better chance
26
+ // of retaining its scroll position upon tab focusing, moving around etc.
27
+ const iframe = document.createElement('iframe');
28
+ this.node.appendChild(iframe);
29
+ // The iframe content window is not available until the onload event.
30
+ iframe.onload = () => {
31
+ const body = iframe.contentWindow!.document.createElement('body');
32
+ body.style.margin = '0px';
33
+ iframe.contentWindow!.document.body = body;
34
+ this._object = iframe.contentWindow!.document.createElement('object');
35
+ // work around for https://discussions.apple.com/thread/252247740
36
+ // Detect if running on Desktop Safari
37
+ if (!(window as any).safari) {
38
+ this._object.type = MIME_TYPE;
39
+ }
40
+ this._object.width = '100%';
41
+ this._object.height = '100%';
42
+ body.appendChild(this._object);
43
+ this._ready.resolve(void 0);
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Render PDF into this widget's node.
49
+ */
50
+ async renderModel(model: IRenderMime.IMimeModel): Promise<void> {
51
+ await this._ready.promise;
52
+ const data = model.data[MIME_TYPE] as string | undefined;
53
+ if (
54
+ !data ||
55
+ (data.length === this._base64.length && data === this._base64)
56
+ ) {
57
+ // If there is no data, or if the string has not changed, we do not
58
+ // need to re-parse the data and rerender. We do, however, check
59
+ // for a fragment if the user wants to scroll the output.
60
+ if (model.metadata.fragment && this._object.data) {
61
+ const url = this._object.data;
62
+ this._object.data = `${url.split('#')[0]}${model.metadata.fragment}`;
63
+ }
64
+ // For some opaque reason, Firefox seems to loose its scroll position
65
+ // upon unhiding a PDF. But triggering a refresh of the URL makes it
66
+ // find it again. No idea what the reason for this is.
67
+ if (Private.IS_FIREFOX) {
68
+ this._object.data = this._object.data; // eslint-disable-line
69
+ }
70
+ return Promise.resolve(void 0);
71
+ }
72
+ this._base64 = data;
73
+ const blob = Private.b64toBlob(data, MIME_TYPE);
74
+
75
+ // Release reference to any previous object url.
76
+ if (this._disposable) {
77
+ this._disposable.dispose();
78
+ }
79
+ let objectUrl = URL.createObjectURL(blob);
80
+ if (model.metadata.fragment) {
81
+ objectUrl += model.metadata.fragment;
82
+ }
83
+ this._object.data = objectUrl;
84
+
85
+ // Set the disposable release the object URL.
86
+ this._disposable = new DisposableDelegate(() => {
87
+ try {
88
+ URL.revokeObjectURL(objectUrl);
89
+ } catch (error) {
90
+ /* no-op */
91
+ }
92
+ });
93
+ return;
94
+ }
95
+
96
+ /**
97
+ * Handle a `before-hide` message.
98
+ */
99
+ protected onBeforeHide(): void {
100
+ // Dispose of any URL fragment before hiding the widget
101
+ // so that it is not remembered upon show. Only Firefox
102
+ // seems to have a problem with this.
103
+ if (Private.IS_FIREFOX) {
104
+ this._object.data = this._object.data.split('#')[0];
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Dispose of the resources held by the pdf widget.
110
+ */
111
+ dispose(): void {
112
+ if (this._disposable) {
113
+ this._disposable.dispose();
114
+ }
115
+ super.dispose();
116
+ }
117
+
118
+ private _base64 = '';
119
+ private _disposable: DisposableDelegate | null = null;
120
+ private _object: HTMLObjectElement;
121
+ private _ready = new PromiseDelegate<void>();
122
+ }
123
+
124
+ /**
125
+ * A mime renderer factory for PDF data.
126
+ */
127
+ export const rendererFactory: IRenderMime.IRendererFactory = {
128
+ safe: false,
129
+ mimeTypes: [MIME_TYPE],
130
+ defaultRank: 100,
131
+ createRenderer: options => new RenderedPDF()
132
+ };
133
+
134
+ const extensions: IRenderMime.IExtension | IRenderMime.IExtension[] = [
135
+ {
136
+ id: '@jupyterlab/pdf-extension:factory',
137
+ rendererFactory,
138
+ dataType: 'string',
139
+ documentWidgetFactoryOptions: {
140
+ name: 'PDF',
141
+ // TODO: translate label
142
+ modelName: 'base64',
143
+ primaryFileType: 'PDF',
144
+ fileTypes: ['PDF'],
145
+ defaultFor: ['PDF']
146
+ }
147
+ }
148
+ ];
149
+
150
+ export default extensions;
151
+
152
+ /**
153
+ * A namespace for PDF widget private data.
154
+ */
155
+ namespace Private {
156
+ /**
157
+ * A flag for determining whether the user is using Firefox.
158
+ * There are some different PDF viewer behaviors on Firefox,
159
+ * and we try to address them with this. User agent string parsing
160
+ * is *not* reliable, so this should be considered a best-effort test.
161
+ */
162
+ export const IS_FIREFOX: boolean = /Firefox/.test(navigator.userAgent);
163
+
164
+ /**
165
+ * Convert a base64 encoded string to a Blob object.
166
+ * Modified from a snippet found here:
167
+ * https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
168
+ *
169
+ * @param b64Data - The base64 encoded data.
170
+ *
171
+ * @param contentType - The mime type of the data.
172
+ *
173
+ * @param sliceSize - The size to chunk the data into for processing.
174
+ *
175
+ * @returns a Blob for the data.
176
+ */
177
+ export function b64toBlob(
178
+ b64Data: string,
179
+ contentType: string = '',
180
+ sliceSize: number = 512
181
+ ): Blob {
182
+ const byteCharacters = atob(b64Data);
183
+ const byteArrays: Uint8Array[] = [];
184
+
185
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
186
+ const slice = byteCharacters.slice(offset, offset + sliceSize);
187
+
188
+ const byteNumbers = new Array(slice.length);
189
+ for (let i = 0; i < slice.length; i++) {
190
+ byteNumbers[i] = slice.charCodeAt(i);
191
+ }
192
+ const byteArray = new Uint8Array(byteNumbers);
193
+ byteArrays.push(byteArray);
194
+ }
195
+
196
+ return new Blob(byteArrays, { type: contentType });
197
+ }
198
+ }
package/style/base.css CHANGED
@@ -17,7 +17,7 @@
17
17
  }
18
18
 
19
19
  /*
20
- When drag events occur, `p-mod-override-cursor` is added to the body.
20
+ When drag events occur, `lm-mod-override-cursor` is added to the body.
21
21
  This reuses the same CSS selector logic as jp-IFrame to prevent embedded
22
22
  PDFs from swallowing cursor events.
23
23
  */