js-draw 1.13.1 → 1.13.2

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.
@@ -42,7 +42,7 @@ const getLocalizationTable_1 = __importDefault(require("./localizations/getLocal
42
42
  const IconProvider_1 = __importDefault(require("./toolbar/IconProvider"));
43
43
  const CanvasRenderer_1 = __importDefault(require("./rendering/renderers/CanvasRenderer"));
44
44
  const untilNextAnimationFrame_1 = __importDefault(require("./util/untilNextAnimationFrame"));
45
- const fileToBase64_1 = __importDefault(require("./util/fileToBase64"));
45
+ const fileToBase64Url_1 = __importDefault(require("./util/fileToBase64Url"));
46
46
  const uniteCommands_1 = __importDefault(require("./commands/uniteCommands"));
47
47
  const SelectionTool_1 = __importDefault(require("./tools/SelectionTool/SelectionTool"));
48
48
  const Erase_1 = __importDefault(require("./commands/Erase"));
@@ -476,7 +476,7 @@ class Editor {
476
476
  this.showLoadingWarning(evt.loaded / evt.total);
477
477
  };
478
478
  try {
479
- const data = await (0, fileToBase64_1.default)(file, onprogress);
479
+ const data = await (0, fileToBase64Url_1.default)(file, { onprogress });
480
480
  if (data && this.toolController.dispatchInputEvent({
481
481
  kind: inputEvents_1.InputEvtType.PasteEvent,
482
482
  mime: fileType,
@@ -9,7 +9,7 @@ const EditorImage_1 = __importDefault(require("../../image/EditorImage"));
9
9
  const uniteCommands_1 = __importDefault(require("../../commands/uniteCommands"));
10
10
  const SelectionTool_1 = __importDefault(require("../../tools/SelectionTool/SelectionTool"));
11
11
  const math_1 = require("@js-draw/math");
12
- const fileToBase64_1 = __importDefault(require("../../util/fileToBase64"));
12
+ const fileToBase64Url_1 = __importDefault(require("../../util/fileToBase64Url"));
13
13
  const BaseWidget_1 = __importDefault(require("./BaseWidget"));
14
14
  const types_1 = require("../../types");
15
15
  const constants_1 = require("../constants");
@@ -111,11 +111,13 @@ class InsertImageWidget extends BaseWidget_1.default {
111
111
  this.imagePreview.style.display = 'block';
112
112
  const image = files[0];
113
113
  let data = null;
114
+ let errorMessage = null;
114
115
  try {
115
- data = await (0, fileToBase64_1.default)(image);
116
+ data = await (0, fileToBase64Url_1.default)(image);
116
117
  }
117
- catch (e) {
118
- this.statusView.innerText = this.localizationTable.imageLoadError(e);
118
+ catch (error) {
119
+ console.error('Image load error', error);
120
+ errorMessage = this.localizationTable.imageLoadError(error);
119
121
  }
120
122
  if (data) {
121
123
  this.image = ImageWrapper.fromSrcAndPreview(data, this.imagePreview, () => this.onImageDataUpdate());
@@ -124,6 +126,11 @@ class InsertImageWidget extends BaseWidget_1.default {
124
126
  this.image = null;
125
127
  }
126
128
  this.onImageDataUpdate();
129
+ // Show the error after image update callbacks to ensure it is
130
+ // actually shown.
131
+ if (errorMessage) {
132
+ this.statusView.innerText = errorMessage;
133
+ }
127
134
  });
128
135
  altTextRow.replaceChildren(imageAltTextLabel, this.imageAltTextInput);
129
136
  actionButtonRow.replaceChildren(this.submitButton);
@@ -0,0 +1,9 @@
1
+ export interface FileToBase64UrlOptions {
2
+ onprogress?: (evt: ProgressEvent<FileReader>) => void;
3
+ onWarning?: (message: string, error: any) => void;
4
+ }
5
+ /**
6
+ * Converts `file` to a base64 data URL.
7
+ */
8
+ declare const fileToBase64Url: (file: Blob, options?: FileToBase64UrlOptions) => Promise<string | null>;
9
+ export default fileToBase64Url;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Converts `file` to a base64 data URL.
5
+ */
6
+ const fileToBase64Url = async (file, options = {}) => {
7
+ try {
8
+ const reader = new FileReader();
9
+ return await new Promise((resolve, reject) => {
10
+ reader.onload = () => resolve(reader.result);
11
+ reader.onerror = reject;
12
+ reader.onabort = reject;
13
+ reader.onprogress = (evt) => {
14
+ options.onprogress?.(evt);
15
+ };
16
+ reader.readAsDataURL(file);
17
+ });
18
+ }
19
+ catch (error) {
20
+ // Files can fail to load with a FileReader in some cases. For example,
21
+ // in iOS Lockdown mode, where FileReader is unavailable.
22
+ (options.onWarning ?? console.warn)('Unable to convert file to base64 with a FileReader: ', error);
23
+ const arrayBuffer = await file.arrayBuffer();
24
+ const array = new Uint8Array(arrayBuffer);
25
+ // step: must be divisible by 3 (3 bytes = 4 base64 numerals)
26
+ // If too large, this will fail (String.fromCharCode accepts a limited
27
+ // number of arguments).
28
+ const step = 30;
29
+ const result = [];
30
+ for (let i = 0; i < array.length; i += step) {
31
+ // btoa accepts only characters with byte value 0-255 (which can be created
32
+ // with String.fromCharCode)
33
+ const stringByteArray = String.fromCharCode(...array.slice(i, i + step));
34
+ result.push(btoa(stringByteArray));
35
+ }
36
+ return `data:${file.type ?? 'image/*'};base64,${result.join('')}`;
37
+ }
38
+ };
39
+ exports.default = fileToBase64Url;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
- number: '1.13.1',
4
+ number: '1.13.2',
5
5
  };
@@ -13,7 +13,7 @@ import getLocalizationTable from './localizations/getLocalizationTable.mjs';
13
13
  import IconProvider from './toolbar/IconProvider.mjs';
14
14
  import CanvasRenderer from './rendering/renderers/CanvasRenderer.mjs';
15
15
  import untilNextAnimationFrame from './util/untilNextAnimationFrame.mjs';
16
- import fileToBase64 from './util/fileToBase64.mjs';
16
+ import fileToBase64Url from './util/fileToBase64Url.mjs';
17
17
  import uniteCommands from './commands/uniteCommands.mjs';
18
18
  import SelectionTool from './tools/SelectionTool/SelectionTool.mjs';
19
19
  import Erase from './commands/Erase.mjs';
@@ -447,7 +447,7 @@ export class Editor {
447
447
  this.showLoadingWarning(evt.loaded / evt.total);
448
448
  };
449
449
  try {
450
- const data = await fileToBase64(file, onprogress);
450
+ const data = await fileToBase64Url(file, { onprogress });
451
451
  if (data && this.toolController.dispatchInputEvent({
452
452
  kind: InputEvtType.PasteEvent,
453
453
  mime: fileType,
@@ -4,7 +4,7 @@ import EditorImage from '../../image/EditorImage.mjs';
4
4
  import uniteCommands from '../../commands/uniteCommands.mjs';
5
5
  import SelectionTool from '../../tools/SelectionTool/SelectionTool.mjs';
6
6
  import { Mat33 } from '@js-draw/math';
7
- import fileToBase64 from '../../util/fileToBase64.mjs';
7
+ import fileToBase64Url from '../../util/fileToBase64Url.mjs';
8
8
  import BaseWidget from './BaseWidget.mjs';
9
9
  import { EditorEventType } from '../../types.mjs';
10
10
  import { toolbarCSSPrefix } from '../constants.mjs';
@@ -106,11 +106,13 @@ class InsertImageWidget extends BaseWidget {
106
106
  this.imagePreview.style.display = 'block';
107
107
  const image = files[0];
108
108
  let data = null;
109
+ let errorMessage = null;
109
110
  try {
110
- data = await fileToBase64(image);
111
+ data = await fileToBase64Url(image);
111
112
  }
112
- catch (e) {
113
- this.statusView.innerText = this.localizationTable.imageLoadError(e);
113
+ catch (error) {
114
+ console.error('Image load error', error);
115
+ errorMessage = this.localizationTable.imageLoadError(error);
114
116
  }
115
117
  if (data) {
116
118
  this.image = ImageWrapper.fromSrcAndPreview(data, this.imagePreview, () => this.onImageDataUpdate());
@@ -119,6 +121,11 @@ class InsertImageWidget extends BaseWidget {
119
121
  this.image = null;
120
122
  }
121
123
  this.onImageDataUpdate();
124
+ // Show the error after image update callbacks to ensure it is
125
+ // actually shown.
126
+ if (errorMessage) {
127
+ this.statusView.innerText = errorMessage;
128
+ }
122
129
  });
123
130
  altTextRow.replaceChildren(imageAltTextLabel, this.imageAltTextInput);
124
131
  actionButtonRow.replaceChildren(this.submitButton);
@@ -0,0 +1,9 @@
1
+ export interface FileToBase64UrlOptions {
2
+ onprogress?: (evt: ProgressEvent<FileReader>) => void;
3
+ onWarning?: (message: string, error: any) => void;
4
+ }
5
+ /**
6
+ * Converts `file` to a base64 data URL.
7
+ */
8
+ declare const fileToBase64Url: (file: Blob, options?: FileToBase64UrlOptions) => Promise<string | null>;
9
+ export default fileToBase64Url;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Converts `file` to a base64 data URL.
3
+ */
4
+ const fileToBase64Url = async (file, options = {}) => {
5
+ try {
6
+ const reader = new FileReader();
7
+ return await new Promise((resolve, reject) => {
8
+ reader.onload = () => resolve(reader.result);
9
+ reader.onerror = reject;
10
+ reader.onabort = reject;
11
+ reader.onprogress = (evt) => {
12
+ options.onprogress?.(evt);
13
+ };
14
+ reader.readAsDataURL(file);
15
+ });
16
+ }
17
+ catch (error) {
18
+ // Files can fail to load with a FileReader in some cases. For example,
19
+ // in iOS Lockdown mode, where FileReader is unavailable.
20
+ (options.onWarning ?? console.warn)('Unable to convert file to base64 with a FileReader: ', error);
21
+ const arrayBuffer = await file.arrayBuffer();
22
+ const array = new Uint8Array(arrayBuffer);
23
+ // step: must be divisible by 3 (3 bytes = 4 base64 numerals)
24
+ // If too large, this will fail (String.fromCharCode accepts a limited
25
+ // number of arguments).
26
+ const step = 30;
27
+ const result = [];
28
+ for (let i = 0; i < array.length; i += step) {
29
+ // btoa accepts only characters with byte value 0-255 (which can be created
30
+ // with String.fromCharCode)
31
+ const stringByteArray = String.fromCharCode(...array.slice(i, i + step));
32
+ result.push(btoa(stringByteArray));
33
+ }
34
+ return `data:${file.type ?? 'image/*'};base64,${result.join('')}`;
35
+ }
36
+ };
37
+ export default fileToBase64Url;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,3 @@
1
1
  export default {
2
- number: '1.13.1',
2
+ number: '1.13.2',
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -86,5 +86,5 @@
86
86
  "freehand",
87
87
  "svg"
88
88
  ],
89
- "gitHead": "814f5d57897fd16fd45860522d9ecbc6b8b04a38"
89
+ "gitHead": "9a48e4d746783977e2b2808be9d97f3521fb830f"
90
90
  }
@@ -1,3 +0,0 @@
1
- type ProgressListener = (evt: ProgressEvent<FileReader>) => void;
2
- declare const fileToBase64: (file: File, onprogress?: ProgressListener) => Promise<string | null>;
3
- export default fileToBase64;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const fileToBase64 = (file, onprogress) => {
4
- const reader = new FileReader();
5
- return new Promise((resolve, reject) => {
6
- reader.onload = () => resolve(reader.result);
7
- reader.onerror = reject;
8
- reader.onabort = reject;
9
- reader.onprogress = (evt) => {
10
- onprogress?.(evt);
11
- };
12
- reader.readAsDataURL(file);
13
- });
14
- };
15
- exports.default = fileToBase64;
@@ -1,3 +0,0 @@
1
- type ProgressListener = (evt: ProgressEvent<FileReader>) => void;
2
- declare const fileToBase64: (file: File, onprogress?: ProgressListener) => Promise<string | null>;
3
- export default fileToBase64;
@@ -1,13 +0,0 @@
1
- const fileToBase64 = (file, onprogress) => {
2
- const reader = new FileReader();
3
- return new Promise((resolve, reject) => {
4
- reader.onload = () => resolve(reader.result);
5
- reader.onerror = reject;
6
- reader.onabort = reject;
7
- reader.onprogress = (evt) => {
8
- onprogress?.(evt);
9
- };
10
- reader.readAsDataURL(file);
11
- });
12
- };
13
- export default fileToBase64;