graphico 1.2.0 → 1.3.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/dist/canvas.js CHANGED
@@ -1,79 +1,117 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Canvas = void 0;
4
1
  /**
5
2
  * Represents a canvas for drawing and animating
6
3
  */
7
- var Canvas = /** @class */ (function () {
4
+ export class Canvas {
5
+ /**
6
+ * Default canvas options
7
+ */
8
+ static defaults = {
9
+ debug: false,
10
+ parent: document.body,
11
+ width: 600,
12
+ height: 400,
13
+ scale: 1,
14
+ background: 'white',
15
+ border: 'transparent',
16
+ borderBlur: 'transparent',
17
+ showMouse: true,
18
+ keepFocused: false,
19
+ numLayers: 1,
20
+ numTracks: 1,
21
+ keydown() { return; },
22
+ keyup() { return; },
23
+ mousemove() { return; },
24
+ mousedown() { return; },
25
+ mouseup() { return; },
26
+ loop() { return; },
27
+ };
28
+ /**
29
+ * Configuration options for this canvas
30
+ */
31
+ config;
32
+ /**
33
+ * Used to render 2D objects onto the main canvas
34
+ */
35
+ graphic;
36
+ /**
37
+ * Can be used to render 2D graphics onto layers of the canvas
38
+ */
39
+ layers = [];
40
+ /**
41
+ * Contains a list of current keys pressed
42
+ */
43
+ keys = [];
44
+ /**
45
+ * Contains a list of current mouse buttons pressed
46
+ */
47
+ mouseButtons = [];
48
+ /**
49
+ * The width of the canvas, in pixels
50
+ */
51
+ width;
52
+ /**
53
+ * The height of the canvas, in pixels
54
+ */
55
+ height;
56
+ /**
57
+ * Contains the mouse X-coordinate, in pixels
58
+ */
59
+ mouseX = 0;
60
+ /**
61
+ * Contains the mouse Y-coordinate, in pixels
62
+ */
63
+ mouseY = 0;
64
+ /**
65
+ * Represents the animation ID handle to cancel the animation
66
+ */
67
+ animation = 0;
68
+ /**
69
+ * The last frame's high resolution timestamp
70
+ */
71
+ lastFrame = 0;
72
+ /**
73
+ * Determine whether the client is focused or not
74
+ */
75
+ focused = false;
76
+ /**
77
+ * Determine whether the user has interacted with this canvas
78
+ */
79
+ interacted = false;
80
+ /**
81
+ * The media recording object for screen captures
82
+ */
83
+ recorder;
84
+ /**
85
+ * Determine whether audio is allowed or muted
86
+ */
87
+ muted = false;
88
+ /**
89
+ * Contains the list of all active audio elements
90
+ */
91
+ audios = [];
8
92
  /**
9
93
  * Create a new canvas with the provided options
10
94
  * @param options Configuration options
11
95
  */
12
- function Canvas(options) {
13
- if (options === void 0) { options = {}; }
14
- var _this = this;
15
- /**
16
- * Can be used to render 2D graphics onto layers of the canvas
17
- */
18
- this.layers = [];
19
- /**
20
- * Contains a list of current keys pressed
21
- */
22
- this.keys = [];
23
- /**
24
- * Contains a list of current mouse buttons pressed
25
- */
26
- this.mouseButtons = [];
27
- /**
28
- * Contains the mouse X-coordinate, in pixels
29
- */
30
- this.mouseX = 0;
31
- /**
32
- * Contains the mouse Y-coordinate, in pixels
33
- */
34
- this.mouseY = 0;
35
- /**
36
- * Represents the animation ID handle to cancel the animation
37
- */
38
- this.animation = 0;
39
- /**
40
- * The last frame's high resolution timestamp
41
- */
42
- this.lastFrame = 0;
43
- /**
44
- * Determine whether the client is focused or not
45
- */
46
- this.focused = false;
47
- /**
48
- * Determine whether the user has interacted with this canvas
49
- */
50
- this.interacted = false;
51
- /**
52
- * Determine whether audio is allowed or muted
53
- */
54
- this.muted = false;
55
- /**
56
- * Contains the list of all active audio elements
57
- */
58
- this.audios = [];
96
+ constructor(options = {}) {
59
97
  this.config = Canvas.setDefaults(options, Canvas.defaults);
60
98
  this.width = this.config.width;
61
99
  this.height = this.config.height;
62
100
  // Create the main <canvas> element and set properties
63
- var main = document.createElement('canvas');
101
+ const main = document.createElement('canvas');
64
102
  main.tabIndex = 1; // For element focus
65
103
  main.style.outline = 'none';
66
104
  main.style.imageRendering = 'pixelated';
67
105
  main.width = this.config.width;
68
106
  main.height = this.config.height;
69
- main.style.width = "".concat(this.config.width * this.config.scale, "px");
70
- main.style.height = "".concat(this.config.height * this.config.scale, "px");
71
- main.style.border = "".concat(this.config.scale, "px solid ").concat(this.config.border);
107
+ main.style.width = `${this.config.width * this.config.scale}px`;
108
+ main.style.height = `${this.config.height * this.config.scale}px`;
109
+ main.style.border = `${this.config.scale}px solid ${this.config.border}`;
72
110
  main.style.cursor = this.config.showMouse ? 'default' : 'none';
73
111
  main.style.boxSizing = 'content-box';
74
112
  this.config.parent.appendChild(main);
75
113
  // Create main canvas graphics object
76
- var graphics = main.getContext('2d');
114
+ const graphics = main.getContext('2d');
77
115
  if (graphics) {
78
116
  this.graphic = graphics;
79
117
  }
@@ -82,9 +120,9 @@ var Canvas = /** @class */ (function () {
82
120
  }
83
121
  graphics.imageSmoothingEnabled = false;
84
122
  // Create canvas layers
85
- for (var i = 0; i < this.config.numLayers; i++) {
86
- var canvas = document.createElement('canvas');
87
- var layer = canvas.getContext('2d');
123
+ for (let i = 0; i < this.config.numLayers; i++) {
124
+ const canvas = document.createElement('canvas');
125
+ const layer = canvas.getContext('2d');
88
126
  if (layer) {
89
127
  this.layers.push(layer);
90
128
  }
@@ -98,124 +136,123 @@ var Canvas = /** @class */ (function () {
98
136
  layer.imageSmoothingEnabled = false;
99
137
  }
100
138
  // Event listeners
101
- main.addEventListener('mousemove', function (e) {
102
- if (!_this.focused) {
139
+ main.addEventListener('mousemove', e => {
140
+ if (!this.focused) {
103
141
  return;
104
142
  }
105
- _this.mouseX = (e.offsetX / _this.config.scale) | 0;
106
- _this.mouseY = (e.offsetY / _this.config.scale) | 0;
107
- _this.log(e.type, _this.mouseX, _this.mouseY);
108
- _this.config.mousemove(_this.mouseX, _this.mouseY);
143
+ this.mouseX = (e.offsetX / this.config.scale) | 0;
144
+ this.mouseY = (e.offsetY / this.config.scale) | 0;
145
+ this.log(e.type, this.mouseX, this.mouseY);
146
+ this.config.mousemove(this.mouseX, this.mouseY);
109
147
  });
110
- main.addEventListener('keydown', function (e) {
148
+ main.addEventListener('keydown', e => {
111
149
  audioContext.resume();
112
- _this.interacted = true;
113
- if (!_this.focused) {
150
+ this.interacted = true;
151
+ if (!this.focused) {
114
152
  return;
115
153
  }
116
154
  e.preventDefault();
117
- var key = e.key.toLowerCase();
118
- _this.log(e.type, _this.keys);
119
- if (!_this.keys.includes(key)) {
120
- _this.keys.push(key);
121
- _this.config.keydown(key);
155
+ const key = e.key.toLowerCase();
156
+ this.log(e.type, this.keys);
157
+ if (!this.keys.includes(key)) {
158
+ this.keys.push(key);
159
+ this.config.keydown(key);
122
160
  }
123
161
  });
124
- main.addEventListener('keyup', function (e) {
125
- if (!_this.focused) {
162
+ main.addEventListener('keyup', e => {
163
+ if (!this.focused) {
126
164
  return;
127
165
  }
128
- var key = e.key.toLowerCase();
129
- var index = _this.keys.indexOf(key);
130
- _this.log(e.type, _this.keys);
166
+ const key = e.key.toLowerCase();
167
+ const index = this.keys.indexOf(key);
168
+ this.log(e.type, this.keys);
131
169
  if (index >= 0) {
132
- _this.keys.splice(index, 1);
133
- _this.config.keyup(key);
170
+ this.keys.splice(index, 1);
171
+ this.config.keyup(key);
134
172
  }
135
173
  });
136
- main.addEventListener('mousedown', function (e) {
174
+ main.addEventListener('mousedown', e => {
137
175
  audioContext.resume();
138
- _this.interacted = true;
139
- if (!_this.focused) {
176
+ this.interacted = true;
177
+ if (!this.focused) {
140
178
  return;
141
179
  }
142
- var button = e.button;
143
- _this.log(e.type, _this.mouseButtons);
144
- if (!_this.mouseButtons.includes(button)) {
145
- _this.mouseButtons.push(button);
146
- _this.config.mousedown(button);
180
+ const button = e.button;
181
+ this.log(e.type, this.mouseButtons);
182
+ if (!this.mouseButtons.includes(button)) {
183
+ this.mouseButtons.push(button);
184
+ this.config.mousedown(button);
147
185
  }
148
186
  });
149
- main.addEventListener('mouseup', function (e) {
150
- if (!_this.focused) {
187
+ main.addEventListener('mouseup', e => {
188
+ if (!this.focused) {
151
189
  return;
152
190
  }
153
- var button = e.button;
154
- var index = _this.mouseButtons.indexOf(button);
155
- _this.log(e.type, _this.mouseButtons);
191
+ const button = e.button;
192
+ const index = this.mouseButtons.indexOf(button);
193
+ this.log(e.type, this.mouseButtons);
156
194
  if (index >= 0) {
157
- _this.mouseButtons.splice(index, 1);
158
- _this.config.mouseup(button);
195
+ this.mouseButtons.splice(index, 1);
196
+ this.config.mouseup(button);
159
197
  }
160
198
  });
161
- main.addEventListener('focusin', function (e) {
162
- _this.focused = true;
163
- main.style.borderColor = _this.config.border;
164
- _this.log(e.type, _this.focused);
165
- _this.animation = requestAnimationFrame(function (time) { return _this.startAnimate(time); });
199
+ main.addEventListener('focusin', e => {
200
+ this.focused = true;
201
+ main.style.borderColor = this.config.border;
202
+ this.log(e.type, this.focused);
203
+ this.animation = requestAnimationFrame(time => this.startAnimate(time));
166
204
  });
167
- main.addEventListener('focusout', function (e) {
168
- if (_this.config.keepFocused) {
205
+ main.addEventListener('focusout', e => {
206
+ if (this.config.keepFocused) {
169
207
  main.focus();
170
208
  }
171
209
  else {
172
- _this.focused = false;
173
- main.style.borderColor = _this.config.borderBlur;
174
- _this.log(e.type, _this.focused);
175
- cancelAnimationFrame(_this.animation);
210
+ this.focused = false;
211
+ main.style.borderColor = this.config.borderBlur;
212
+ this.log(e.type, this.focused);
213
+ cancelAnimationFrame(this.animation);
176
214
  }
177
215
  });
178
- window.addEventListener('blur', function (e) {
179
- _this.log(e.type);
180
- cancelAnimationFrame(_this.animation);
216
+ window.addEventListener('blur', e => {
217
+ this.log(e.type);
218
+ cancelAnimationFrame(this.animation);
181
219
  });
182
- main.addEventListener('contextmenu', function (e) { return e.preventDefault(); });
220
+ main.addEventListener('contextmenu', e => e.preventDefault());
183
221
  // Initialize audio tracks for recording
184
- var stream = new MediaStream(main.captureStream()); // video stream
185
- var audioContext = new window.AudioContext();
186
- for (var track = 0; track < this.config.numTracks; track++) {
187
- var audio = new Audio();
188
- var source = audioContext.createMediaElementSource(audio);
189
- var destination = audioContext.createMediaStreamDestination();
222
+ const stream = new MediaStream(main.captureStream()); // video stream
223
+ const audioContext = new window.AudioContext();
224
+ for (let track = 0; track < this.config.numTracks; track++) {
225
+ const audio = new Audio();
226
+ const source = audioContext.createMediaElementSource(audio);
227
+ const destination = audioContext.createMediaStreamDestination();
190
228
  source.connect(destination); // contains the audio stream for recording
191
229
  source.connect(audioContext.destination); // so the user can hear
192
230
  this.audios.push(audio);
193
- for (var _i = 0, _a = destination.stream.getAudioTracks(); _i < _a.length; _i++) {
194
- var audioTrack = _a[_i];
195
- this.log("Adding ".concat(audioTrack.id, " to track ").concat(track, "."));
231
+ for (const audioTrack of destination.stream.getAudioTracks()) {
232
+ this.log(`Adding ${audioTrack.id} to track ${track}.`);
196
233
  stream.addTrack(audioTrack); // add audio track
197
234
  }
198
235
  }
199
236
  // Initialize the media recorder
200
- var recordPart = [];
237
+ const recordPart = [];
201
238
  this.recorder = new MediaRecorder(stream);
202
- this.recorder.addEventListener('dataavailable', function (e) {
203
- _this.log(e.type);
239
+ this.recorder.addEventListener('dataavailable', e => {
240
+ this.log(e.type);
204
241
  recordPart.push(e.data);
205
242
  });
206
- this.recorder.addEventListener('stop', function (e) {
207
- _this.log(e.type);
208
- var recording = new Blob(recordPart, { type: 'video/webm;codecs=h264' });
209
- var url = URL.createObjectURL(recording);
210
- var downloadLink = document.createElement('a');
243
+ this.recorder.addEventListener('stop', e => {
244
+ this.log(e.type);
245
+ const recording = new Blob(recordPart, { type: 'video/webm;codecs=h264' });
246
+ const url = URL.createObjectURL(recording);
247
+ const downloadLink = document.createElement('a');
211
248
  downloadLink.setAttribute('href', url);
212
- downloadLink.setAttribute('download', "recording-".concat(Date.now()));
249
+ downloadLink.setAttribute('download', `recording-${Date.now()}`);
213
250
  downloadLink.click();
214
251
  // Clear out the existing blob parts for recording a new capture
215
252
  recordPart.splice(0);
216
253
  // Remove all keys and mouse buttons down because we lose focus
217
- _this.keys.splice(0);
218
- _this.mouseButtons.splice(0);
254
+ this.keys.splice(0);
255
+ this.mouseButtons.splice(0);
219
256
  });
220
257
  // Focus on the canvas
221
258
  main.focus();
@@ -223,91 +260,87 @@ var Canvas = /** @class */ (function () {
223
260
  /**
224
261
  * Start the animation.
225
262
  */
226
- Canvas.prototype.startAnimate = function (time) {
227
- var _this = this;
263
+ startAnimate(time) {
228
264
  this.log('startAnimate', time);
229
265
  this.lastFrame = time;
230
- this.animation = requestAnimationFrame(function (time) { return _this.animate(time); });
231
- };
266
+ this.animation = requestAnimationFrame(time => this.animate(time));
267
+ }
232
268
  /**
233
269
  * Run the main animation loop.
234
270
  */
235
- Canvas.prototype.animate = function (time) {
236
- var _this = this;
237
- var currentFrame = time;
238
- var dt = currentFrame - this.lastFrame;
271
+ animate(time) {
272
+ const currentFrame = time;
273
+ const dt = currentFrame - this.lastFrame;
239
274
  this.lastFrame = currentFrame;
240
275
  this.config.loop(dt);
241
276
  // Draw all the layers onto the main canvas
242
277
  this.graphic.fillStyle = this.config.background;
243
278
  this.graphic.fillRect(0, 0, this.width, this.height);
244
- for (var _i = 0, _a = this.layers; _i < _a.length; _i++) {
245
- var layer = _a[_i];
279
+ for (const layer of this.layers) {
246
280
  this.graphic.drawImage(layer.canvas, 0, 0);
247
281
  }
248
- this.animation = requestAnimationFrame(function (time) { return _this.animate(time); });
249
- };
282
+ this.animation = requestAnimationFrame(time => this.animate(time));
283
+ }
250
284
  /**
251
285
  * Determine whether a key is currently pressed.
252
286
  * @param key The key to check
253
287
  * @returns True if `key` is down
254
288
  */
255
- Canvas.prototype.isKeyDown = function (key) {
289
+ isKeyDown(key) {
256
290
  return this.keys.includes(key.toLowerCase());
257
- };
291
+ }
258
292
  /**
259
293
  * Determine whether a mouse button is currently pressed.
260
294
  * @param button The button ID
261
295
  * @returns True if `button` is down
262
296
  */
263
- Canvas.prototype.isMouseButtonDown = function (button) {
297
+ isMouseButtonDown(button) {
264
298
  return this.mouseButtons.includes(button);
265
- };
299
+ }
266
300
  /**
267
301
  * Get the current cursor position.
268
302
  * @returns Cursor position as `[x, y]`
269
303
  */
270
- Canvas.prototype.getMousePosition = function () {
304
+ getMousePosition() {
271
305
  return [this.mouseX, this.mouseY];
272
- };
306
+ }
273
307
  /**
274
308
  * Take a screenshot of the canvas contents and save to a .png file.
275
309
  * @param name The file name of the screenshot
276
310
  */
277
- Canvas.prototype.screenshot = function (name) {
278
- if (name === void 0) { name = 'screenshot'; }
311
+ screenshot(name = 'screenshot') {
279
312
  // Generate a data URL and set it as the download parameter for <a>
280
- var dataURL = this.graphic.canvas.toDataURL();
281
- var downloadLink = document.createElement('a');
313
+ const dataURL = this.graphic.canvas.toDataURL();
314
+ const downloadLink = document.createElement('a');
282
315
  downloadLink.setAttribute('href', dataURL);
283
316
  downloadLink.setAttribute('download', name);
284
317
  downloadLink.click();
285
318
  // Remove all keys and mouse buttons down because we lose focus
286
319
  this.keys.splice(0);
287
320
  this.mouseButtons.splice(0);
288
- };
321
+ }
289
322
  /**
290
323
  * Start recording all layers on the canvas
291
324
  */
292
- Canvas.prototype.startRecording = function () {
325
+ startRecording() {
293
326
  if (!this.interacted) {
294
327
  throw new Error('The user has not yet interacted with this canvas.');
295
328
  }
296
329
  this.recorder.start();
297
- };
330
+ }
298
331
  /**
299
332
  * Stop recording and download screen capture
300
333
  */
301
- Canvas.prototype.stopRecording = function () {
334
+ stopRecording() {
302
335
  this.recorder.stop();
303
- };
336
+ }
304
337
  /**
305
338
  * Determines whether the media recorder is active
306
339
  * @returns True if currently recording
307
340
  */
308
- Canvas.prototype.isRecording = function () {
341
+ isRecording() {
309
342
  return this.recorder.state === 'recording';
310
- };
343
+ }
311
344
  /**
312
345
  * Play an audio file
313
346
  * @param src The path of the audio file
@@ -315,221 +348,161 @@ var Canvas = /** @class */ (function () {
315
348
  * @param volume The normalized [0-1] volume
316
349
  * @param track The track number to play on
317
350
  */
318
- Canvas.prototype.playAudio = function (src, loop, volume, track) {
319
- if (loop === void 0) { loop = false; }
320
- if (volume === void 0) { volume = 1; }
321
- if (track === void 0) { track = 0; }
322
- this.log("Playing audio on track ".concat(track, "."));
351
+ playAudio(src, loop = false, volume = 1, track = 0) {
352
+ this.log(`Playing audio on track ${track}.`);
323
353
  if (!this.interacted) {
324
354
  throw new Error('The user has not yet interacted with this canvas.');
325
355
  }
326
356
  if (track < 0 || track >= this.config.numTracks) {
327
- throw new Error("Track ".concat(track, " is out of range. [0,").concat(this.config.numTracks, ")"));
357
+ throw new Error(`Track ${track} is out of range. [0,${this.config.numTracks})`);
328
358
  }
329
- var audio = this.audios[track];
359
+ const audio = this.audios[track];
330
360
  audio.src = src;
331
361
  audio.loop = loop;
332
362
  audio.volume = volume;
333
363
  audio.muted = this.muted;
334
364
  audio.play();
335
- };
365
+ }
336
366
  /**
337
367
  * Stop all audio tracks from playing
338
368
  */
339
- Canvas.prototype.stopAudio = function (track) {
340
- if (track === void 0) { track = -1; }
341
- this.log("Stopping audio on track ".concat(track, "."));
369
+ stopAudio(track = -1) {
370
+ this.log(`Stopping audio on track ${track}.`);
342
371
  if (track >= this.config.numTracks) {
343
- throw new Error("Track ".concat(track, " is out of range. [0,").concat(this.config.numTracks, ")"));
372
+ throw new Error(`Track ${track} is out of range. [0,${this.config.numTracks})`);
344
373
  }
345
374
  if (track < 0) {
346
- for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
347
- var audio = _a[_i];
375
+ for (const audio of this.audios) {
348
376
  audio.pause();
349
377
  }
350
378
  }
351
379
  else {
352
380
  this.audios[track].pause();
353
381
  }
354
- };
382
+ }
355
383
  /**
356
384
  * Mute all audio
357
385
  */
358
- Canvas.prototype.mute = function () {
386
+ mute() {
359
387
  this.muted = true;
360
- for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
361
- var audio = _a[_i];
388
+ for (const audio of this.audios) {
362
389
  audio.muted = this.muted;
363
390
  }
364
- };
391
+ }
365
392
  /**
366
393
  * Unmute all audio
367
394
  */
368
- Canvas.prototype.unmute = function () {
395
+ unmute() {
369
396
  this.muted = false;
370
- for (var _i = 0, _a = this.audios; _i < _a.length; _i++) {
371
- var audio = _a[_i];
397
+ for (const audio of this.audios) {
372
398
  audio.muted = this.muted;
373
399
  }
374
- };
400
+ }
375
401
  /**
376
402
  * Determines whether audio is muted
377
403
  * @returns True if currently muted
378
404
  */
379
- Canvas.prototype.isMuted = function () {
405
+ isMuted() {
380
406
  return this.muted;
381
- };
407
+ }
382
408
  /**
383
409
  * Draw an object onto the canvas.
384
410
  * @param drawable Any drawable object
385
411
  * @param layer The zero-indexed layer to draw to
386
412
  */
387
- Canvas.prototype.draw = function (drawable, layer) {
388
- if (layer === void 0) { layer = 0; }
413
+ draw(drawable, layer = 0) {
389
414
  if (layer < 0 || layer >= this.config.numLayers) {
390
- throw new Error("Layer ".concat(layer, " is out of range. [0,").concat(this.config.numLayers, ")"));
415
+ throw new Error(`Layer ${layer} is out of range. [0,${this.config.numLayers})`);
391
416
  }
392
417
  drawable.draw(this.layers[layer]);
393
- };
418
+ }
394
419
  /**
395
420
  * Completely clears the canvas.
396
421
  * @param layer The zero-indexed layer to clear, if unset, will clear all layers
397
422
  */
398
- Canvas.prototype.clear = function (layer) {
399
- if (layer === void 0) { layer = -1; }
423
+ clear(layer = -1) {
400
424
  if (layer >= this.config.numLayers) {
401
- throw new Error("Layer ".concat(layer, " is out of range. [0,").concat(this.config.numLayers, ")"));
425
+ throw new Error(`Layer ${layer} is out of range. [0,${this.config.numLayers})`);
402
426
  }
403
427
  if (layer < 0) {
404
- for (var _i = 0, _a = this.layers; _i < _a.length; _i++) {
405
- var layer_1 = _a[_i];
406
- layer_1.clearRect(0, 0, this.config.width, this.config.height);
428
+ for (const layer of this.layers) {
429
+ layer.clearRect(0, 0, this.config.width, this.config.height);
407
430
  }
408
431
  }
409
432
  else {
410
433
  this.layers[layer].clearRect(0, 0, this.config.width, this.config.height);
411
434
  }
412
- };
435
+ }
413
436
  /**
414
437
  * Save arbitrary data to the browser storage.
415
438
  * **Warning:** This will overwrite any existing data under these object keys.
416
439
  * @param data The arbirary data object to save
417
440
  * @param name An optional "namespace" to store the data, which can be assigned when a user has multiple profiles, for example
418
441
  */
419
- Canvas.prototype.saveData = function (data, name) {
420
- if (name === void 0) { name = 'data'; }
421
- for (var rawkey in data) {
422
- var rawval = data[rawkey];
423
- var key = this.encode([name, rawkey]);
424
- var value = this.encode([rawval, typeof rawval]);
425
- localStorage.setItem(key, value);
426
- this.log("Saved \"".concat(name, ".").concat(rawkey, "\" = \"").concat(rawval, ".").concat(typeof rawval, "\" as \"").concat(key, "\" = \"").concat(value, "\"."));
427
- }
428
- };
442
+ saveData(data, name = 'data') {
443
+ const key = this.encode(name);
444
+ const value = this.encode(data);
445
+ localStorage.setItem(key, value);
446
+ this.log(`Saved "${data}" in ${name}.`);
447
+ }
429
448
  /**
430
449
  * Load arbitrary data from the browser storage.
431
450
  * **Warning:** The loaded data object is not strictly type-checked or sanitized, so it may have missing fields or contain extra fields. It's good practice to manually validate the data once it's loaded from storage.
432
451
  * @param name An optional "namespace" to load data from, assigned when saving data
433
452
  * @returns The data object loaded from browser storage
434
453
  */
435
- Canvas.prototype.loadData = function (name) {
436
- var _a;
437
- if (name === void 0) { name = 'data'; }
438
- var loaded = {};
439
- for (var _i = 0, _b = Object.keys(localStorage); _i < _b.length; _i++) {
440
- var key = _b[_i];
454
+ loadData(name = 'data') {
455
+ const key = this.encode(name);
456
+ const value = localStorage.getItem(key);
457
+ if (value) {
441
458
  try {
442
- var decodedKey = this.decode(key);
443
- var decodedVal = this.decode((_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : '');
444
- if (decodedKey[0] === name && decodedKey.length >= 2 && decodedVal.length >= 1) {
445
- loaded[decodedKey[1]] = decodedVal[0];
446
- this.log("Loaded \"".concat(decodedKey.join('.'), "\" = \"").concat(decodedVal.join('.'), "\" from \"").concat(key, "\""));
447
- }
459
+ const decoded = this.decode(value);
460
+ this.log(`Loaded "${decoded}" from ${name}.`);
461
+ return decoded;
448
462
  }
449
- catch (_c) {
450
- this.log("Skipping \"".concat(key, "\"..."));
463
+ catch {
464
+ this.log(`Failed to load ${name}.`);
451
465
  }
452
466
  }
453
- return loaded;
454
- };
467
+ return undefined;
468
+ }
455
469
  /**
456
470
  * Clear **all** saved data. This action is permanent!
457
471
  * @param name An optional "namespace" to clear data from.
458
472
  */
459
- Canvas.prototype.clearData = function (name) {
460
- if (name === void 0) { name = 'data'; }
461
- for (var _i = 0, _a = Object.keys(localStorage); _i < _a.length; _i++) {
462
- var key = _a[_i];
463
- try {
464
- if (this.decode(key)[0] === name) {
465
- localStorage.removeItem(key);
466
- this.log("Removed ".concat(key, " from storage."));
467
- }
468
- }
469
- catch (_b) {
470
- this.log("Skipping \"".concat(key, "\"..."));
471
- }
472
- }
473
- };
473
+ clearData(name = 'data') {
474
+ const key = this.encode(name);
475
+ localStorage.removeItem(key);
476
+ this.log(`Removed ${name} from storage.`);
477
+ }
474
478
  /**
475
479
  * Encode data for local browser storage.
476
480
  */
477
- Canvas.prototype.encode = function (raw) {
478
- return raw.map(function (item) { return btoa(encodeURIComponent(JSON.stringify(item))); }).join('.');
479
- };
481
+ encode(raw) {
482
+ return btoa(encodeURIComponent(JSON.stringify(raw)));
483
+ }
480
484
  /**
481
485
  * Decode data from local browser storage.
482
486
  */
483
- Canvas.prototype.decode = function (code) {
484
- return code.split('.').map(function (item) { return JSON.parse(decodeURIComponent(atob(item))); });
485
- };
487
+ decode(code) {
488
+ return JSON.parse(decodeURIComponent(atob(code)));
489
+ }
486
490
  /**
487
491
  * Log a message to the debug console.
488
492
  */
489
- Canvas.prototype.log = function () {
490
- var x = [];
491
- for (var _i = 0; _i < arguments.length; _i++) {
492
- x[_i] = arguments[_i];
493
- }
493
+ log(...x) {
494
494
  if (this.config.debug) {
495
- console.log.apply(console, x);
495
+ console.log(...x);
496
496
  }
497
- };
497
+ }
498
498
  /**
499
499
  * Set defaults for all undefined options.
500
500
  */
501
- Canvas.setDefaults = function (options, defaults) {
502
- var _a;
503
- var copy = {};
504
- for (var _i = 0, _b = Object.keys(defaults); _i < _b.length; _i++) {
505
- var key = _b[_i];
506
- copy[key] = (_a = options[key]) !== null && _a !== void 0 ? _a : defaults[key];
501
+ static setDefaults(options, defaults) {
502
+ const copy = {};
503
+ for (const key of Object.keys(defaults)) {
504
+ copy[key] = options[key] ?? defaults[key];
507
505
  }
508
506
  return copy;
509
- };
510
- /**
511
- * Default canvas options
512
- */
513
- Canvas.defaults = {
514
- debug: false,
515
- parent: document.body,
516
- width: 600,
517
- height: 400,
518
- scale: 1,
519
- background: 'white',
520
- border: 'transparent',
521
- borderBlur: 'transparent',
522
- showMouse: true,
523
- keepFocused: false,
524
- numLayers: 1,
525
- numTracks: 1,
526
- keydown: function () { return; },
527
- keyup: function () { return; },
528
- mousemove: function () { return; },
529
- mousedown: function () { return; },
530
- mouseup: function () { return; },
531
- loop: function () { return; },
532
- };
533
- return Canvas;
534
- }());
535
- exports.Canvas = Canvas;
507
+ }
508
+ }
package/dist/index.js CHANGED
@@ -1,19 +1,3 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
1
  /**
18
2
  * @packageDocumentation
19
3
  * Canvas 2D rendering toolkit for games and visual projects
@@ -21,5 +5,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
21
5
  * ![NPM Downloads](https://img.shields.io/npm/d18m/graphico)
22
6
  * ![NPM Last Update](https://img.shields.io/npm/last-update/graphico)
23
7
  */
24
- __exportStar(require("./canvas"), exports);
25
- __exportStar(require("./types"), exports);
8
+ export * from './canvas.js';
9
+ export * from './types.js';
package/dist/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "graphico",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Canvas 2D rendering toolkit for games and visual projects",
5
5
  "homepage": "https://npm.nicfv.com/",
6
+ "type": "module",
6
7
  "bin": "",
7
8
  "main": "dist/index.js",
8
9
  "types": "types/index.d.ts",
@@ -42,6 +43,6 @@
42
43
  "repository": "github:nicfv/npm",
43
44
  "license": "MIT",
44
45
  "devDependencies": {
45
- "t6": "1.2.1"
46
+ "t6": "1.3.0"
46
47
  }
47
48
  }
package/types/canvas.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Drawable, Options, StoreData } from '.';
1
+ import { Drawable, Options } from './index.js';
2
2
  /**
3
3
  * Represents a canvas for drawing and animating
4
4
  */
@@ -161,14 +161,14 @@ export declare class Canvas {
161
161
  * @param data The arbirary data object to save
162
162
  * @param name An optional "namespace" to store the data, which can be assigned when a user has multiple profiles, for example
163
163
  */
164
- saveData<T extends StoreData>(data: T, name?: string): void;
164
+ saveData<T>(data: T, name?: string): void;
165
165
  /**
166
166
  * Load arbitrary data from the browser storage.
167
167
  * **Warning:** The loaded data object is not strictly type-checked or sanitized, so it may have missing fields or contain extra fields. It's good practice to manually validate the data once it's loaded from storage.
168
168
  * @param name An optional "namespace" to load data from, assigned when saving data
169
169
  * @returns The data object loaded from browser storage
170
170
  */
171
- loadData<T extends StoreData>(name?: string): Partial<T>;
171
+ loadData<T>(name?: string): Partial<T> | undefined;
172
172
  /**
173
173
  * Clear **all** saved data. This action is permanent!
174
174
  * @param name An optional "namespace" to clear data from.
package/types/index.d.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  * ![NPM Downloads](https://img.shields.io/npm/d18m/graphico)
6
6
  * ![NPM Last Update](https://img.shields.io/npm/last-update/graphico)
7
7
  */
8
- export * from './canvas';
9
- export * from './types';
8
+ export * from './canvas.js';
9
+ export * from './types.js';
package/types/types.d.ts CHANGED
@@ -93,7 +93,3 @@ export interface Drawable {
93
93
  */
94
94
  draw(graphics: CanvasRenderingContext2D): void;
95
95
  }
96
- /**
97
- * An arbitrary object which can be saved or loaded from browser storage.
98
- */
99
- export type StoreData = Record<string, unknown>;