Skip to content

Commit c3a7b35

Browse files
committed
Remove GIF parser event boilerplate in favor of a @template solution using CustomEvent
1 parent 5aff47c commit c3a7b35

File tree

13 files changed

+97
-187
lines changed

13 files changed

+97
-187
lines changed

image/parsers/gif.js

Lines changed: 25 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import { BitStream } from '../../io/bitstream.js';
1212
import { ByteStream } from '../../io/bytestream.js';
13+
import { createEvent } from './parsers.js';
1314

1415
// https://www.w3.org/Graphics/GIF/spec-gif89a.txt
1516

@@ -29,15 +30,6 @@ export const GifParseEventType = {
2930
* @property {string} version
3031
*/
3132

32-
export class GifHeaderEvent extends Event {
33-
/** @param {GifHeader} header */
34-
constructor(header) {
35-
super(GifParseEventType.HEADER);
36-
/** @type {GifHeader} */
37-
this.header = header;
38-
}
39-
}
40-
4133
/**
4234
* @typedef GifColor
4335
* @property {number} red
@@ -58,15 +50,6 @@ export class GifHeaderEvent extends Event {
5850
* @property {GifColor[]=} globalColorTable Only if globalColorTableFlag is true.
5951
*/
6052

61-
export class GifLogicalScreenEvent extends Event {
62-
/** @param {GifLogicalScreen} */
63-
constructor(logicalScreen) {
64-
super(GifParseEventType.LOGICAL_SCREEN);
65-
/** @type {GifLogicalScreen} */
66-
this.logicalScreen = logicalScreen;
67-
}
68-
}
69-
7053
/**
7154
* @typedef GifTableBasedImage
7255
* @property {number} imageLeftPosition
@@ -82,15 +65,6 @@ export class GifLogicalScreenEvent extends Event {
8265
* @property {Uint8Array} imageData
8366
*/
8467

85-
export class GifTableBasedImageEvent extends Event {
86-
/** @param {GifTableBasedImage} img */
87-
constructor(img) {
88-
super(GifParseEventType.TABLE_BASED_IMAGE);
89-
/** @type {GifTableBasedImage} */
90-
this.tableBasedImage = img;
91-
}
92-
}
93-
9468
/**
9569
* @typedef GifGraphicControlExtension
9670
* @property {number} disposalMethod
@@ -100,29 +74,11 @@ export class GifTableBasedImageEvent extends Event {
10074
* @property {number} transparentColorIndex
10175
*/
10276

103-
export class GifGraphicControlExtensionEvent extends Event {
104-
/** @param {GifGraphicControlExtension} ext */
105-
constructor(ext) {
106-
super(GifParseEventType.GRAPHIC_CONTROL_EXTENSION);
107-
/** @type {GifGraphicControlExtension} */
108-
this.graphicControlExtension = ext;
109-
}
110-
}
111-
11277
/**
11378
* @typedef GifCommentExtension
11479
* @property {string} comment
11580
*/
11681

117-
export class GifCommentExtensionEvent extends Event {
118-
/** @param {string} comment */
119-
constructor(comment) {
120-
super(GifParseEventType.COMMENT_EXTENSION);
121-
/** @type {string} */
122-
this.comment = comment;
123-
}
124-
}
125-
12682
/**
12783
* @typedef GifPlainTextExtension
12884
* @property {number} textGridLeftPosition
@@ -136,37 +92,13 @@ export class GifCommentExtensionEvent extends Event {
13692
* @property {string} plainText
13793
*/
13894

139-
export class GifPlainTextExtensionEvent extends Event {
140-
/** @param {GifPlainTextExtension} ext */
141-
constructor(ext) {
142-
super(GifParseEventType.PLAIN_TEXT_EXTENSION);
143-
/** @type {GifPlainTextExtension} */
144-
this.plainTextExtension = ext;
145-
}
146-
}
147-
14895
/**
14996
* @typedef GifApplicationExtension
15097
* @property {string} applicationIdentifier
15198
* @property {Uint8Array} applicationAuthenticationCode
15299
* @property {Uint8Array} applicationData
153100
*/
154101

155-
export class GifApplicationExtensionEvent extends Event {
156-
/** @param {GifApplicationExtension} ext */
157-
constructor(ext) {
158-
super(GifParseEventType.APPLICATION_EXTENSION);
159-
/** @type {GifApplicationExtension} */
160-
this.applicationExtension = ext;
161-
}
162-
}
163-
164-
export class GifTrailerEvent extends Event {
165-
constructor() {
166-
super(GifParseEventType.TRAILER);
167-
}
168-
}
169-
170102
/**
171103
* The Grammar.
172104
*
@@ -204,8 +136,8 @@ export class GifParser extends EventTarget {
204136
}
205137

206138
/**
207-
* Type-safe way to bind a listener for a GifApplicationExtensionEvent.
208-
* @param {function(GifApplicationExtensionEvent): void} listener
139+
* Type-safe way to bind a listener for a GifApplicationExtension.
140+
* @param {function(CustomEvent<GifApplicationExtension>): void} listener
209141
* @returns {GifParser} for chaining
210142
*/
211143
onApplicationExtension(listener) {
@@ -214,8 +146,8 @@ export class GifParser extends EventTarget {
214146
}
215147

216148
/**
217-
* Type-safe way to bind a listener for a GifCommentExtensionEvent.
218-
* @param {function(GifCommentExtensionEvent): void} listener
149+
* Type-safe way to bind a listener for a GifCommentExtension.
150+
* @param {function(CustomEvent<GifCommentExtension>): void} listener
219151
* @returns {GifParser} for chaining
220152
*/
221153
onCommentExtension(listener) {
@@ -224,8 +156,8 @@ export class GifParser extends EventTarget {
224156
}
225157

226158
/**
227-
* Type-safe way to bind a listener for a GifGraphicControlExtensionEvent.
228-
* @param {function(GifGraphicControlExtensionEvent): void} listener
159+
* Type-safe way to bind a listener for a GifGraphicControlExtension.
160+
* @param {function(CustomEvent<GifGraphicControlExtension>): void} listener
229161
* @returns {GifParser} for chaining
230162
*/
231163
onGraphicControlExtension(listener) {
@@ -234,8 +166,8 @@ export class GifParser extends EventTarget {
234166
}
235167

236168
/**
237-
* Type-safe way to bind a listener for a GifHeaderEvent.
238-
* @param {function(GifHeaderEvent): void} listener
169+
* Type-safe way to bind a listener for a GifHeader.
170+
* @param {function(CustomEvent<GifHeader>): void} listener
239171
* @returns {GifParser} for chaining
240172
*/
241173
onHeader(listener) {
@@ -244,8 +176,8 @@ export class GifParser extends EventTarget {
244176
}
245177

246178
/**
247-
* Type-safe way to bind a listener for a GifLogicalScreenEvent.
248-
* @param {function(GifLogicalScreenEvent): void} listener
179+
* Type-safe way to bind a listener for a GifLogicalScreen.
180+
* @param {function(CustomEvent<GifLogicalScreen>): void} listener
249181
* @returns {GifParser} for chaining
250182
*/
251183
onLogicalScreen(listener) {
@@ -254,8 +186,8 @@ export class GifParser extends EventTarget {
254186
}
255187

256188
/**
257-
* Type-safe way to bind a listener for a GifPlainTextExtensionEvent.
258-
* @param {function(GifPlainTextExtensionEvent): void} listener
189+
* Type-safe way to bind a listener for a GifPlainTextExtension.
190+
* @param {function(CustomEvent<GifPlainTextExtension>): void} listener
259191
* @returns {GifParser} for chaining
260192
*/
261193
onPlainTextExtension(listener) {
@@ -264,8 +196,8 @@ export class GifParser extends EventTarget {
264196
}
265197

266198
/**
267-
* Type-safe way to bind a listener for a GifTableBasedImageEvent.
268-
* @param {function(GifTableBasedImageEvent): void} listener
199+
* Type-safe way to bind a listener for a GifTableBasedImage.
200+
* @param {function(CustomEvent<GifTableBasedImage>): void} listener
269201
* @returns {GifParser} for chaining
270202
*/
271203
onTableBasedImage(listener) {
@@ -274,8 +206,8 @@ export class GifParser extends EventTarget {
274206
}
275207

276208
/**
277-
* Type-safe way to bind a listener for a GifTrailerEvent.
278-
* @param {function(GifTrailerEvent): void} listener
209+
* Type-safe way to bind a listener for the GifTrailer.
210+
* @param {function(CustomEvent): void} listener
279211
* @returns {GifParser} for chaining
280212
*/
281213
onTrailer(listener) {
@@ -294,9 +226,7 @@ export class GifParser extends EventTarget {
294226
const version = this.version = this.bstream.readString(3); // "87a" or "89a"
295227
if (!["87a", "89a"].includes(version)) throw `Bad version: ${version}`;
296228

297-
this.dispatchEvent(new GifHeaderEvent(
298-
/** @type {GifHeader} */ ({ version })
299-
));
229+
this.dispatchEvent(createEvent(GifParseEventType.HEADER, {version}));
300230

301231
// Logical Screen Descriptor.
302232
const logicalScreenWidth = this.bstream.readNumber(2);
@@ -323,7 +253,7 @@ export class GifParser extends EventTarget {
323253
}));
324254
}
325255
}
326-
this.dispatchEvent(new GifLogicalScreenEvent(
256+
this.dispatchEvent(createEvent(GifParseEventType.LOGICAL_SCREEN,
327257
/** @type {GifLogicalScreen} */ ({
328258
logicalScreenWidth,
329259
logicalScreenHeight,
@@ -394,7 +324,7 @@ export class GifParser extends EventTarget {
394324
ptr += arr.byteLength;
395325
}
396326

397-
this.dispatchEvent(new GifTableBasedImageEvent(
327+
this.dispatchEvent(createEvent(GifParseEventType.TABLE_BASED_IMAGE,
398328
/** @type {GifTableBasedImage} */ ({
399329
imageLeftPosition,
400330
imageTopPosition,
@@ -437,7 +367,7 @@ export class GifParser extends EventTarget {
437367
const blockTerminator = this.bstream.readNumber(1);
438368
if (blockTerminator !== 0) throw `GCE: Block terminator of ${blockTerminator}`;
439369

440-
this.dispatchEvent(new GifGraphicControlExtensionEvent(
370+
this.dispatchEvent(createEvent(GifParseEventType.GRAPHIC_CONTROL_EXTENSION,
441371
/** @type {GifGraphicControlExtension} */ ({
442372
disposalMethod,
443373
userInputFlag,
@@ -456,7 +386,7 @@ export class GifParser extends EventTarget {
456386
while ((bytes = this.readSubBlock())) {
457387
comment += new TextDecoder().decode(bytes);
458388
}
459-
this.dispatchEvent(new GifCommentExtensionEvent(comment));
389+
this.dispatchEvent(createEvent(GifParseEventType.COMMENT_EXTENSION, comment));
460390
return true;
461391
}
462392

@@ -479,7 +409,7 @@ export class GifParser extends EventTarget {
479409
plainText += new TextDecoder().decode(bytes);
480410
}
481411

482-
this.dispatchEvent(new GifPlainTextExtensionEvent(
412+
this.dispatchEvent(createEvent(GifParseEventType.PLAIN_TEXT_EXTENSION,
483413
/** @type {GifPlainTextExtension} */ ({
484414
textGridLeftPosition,
485415
textGridTopPosition,
@@ -518,7 +448,7 @@ export class GifParser extends EventTarget {
518448
ptr += arr.byteLength;
519449
}
520450

521-
this.dispatchEvent(new GifApplicationExtensionEvent(
451+
this.dispatchEvent(createEvent(GifParseEventType.APPLICATION_EXTENSION,
522452
/** {@type GifApplicationExtension} */ ({
523453
applicationIdentifier,
524454
applicationAuthenticationCode,
@@ -534,7 +464,7 @@ export class GifParser extends EventTarget {
534464
}
535465
}
536466
else if (nextByte === 0x3B) {
537-
this.dispatchEvent(new GifTrailerEvent());
467+
this.dispatchEvent(createEvent(GifParseEventType.TRAILER));
538468
// Read the trailer.
539469
return false;
540470
}

image/parsers/parsers.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* parsers.js
3+
*
4+
* Common functionality for all image parsers.
5+
*
6+
* Licensed under the MIT License
7+
*
8+
* Copyright(c) 2024 Google Inc.
9+
*/
10+
11+
/**
12+
* Creates a new event of the given type with the specified data.
13+
* @template T
14+
* @param {string} type The event type.
15+
* @param {T} data The event data.
16+
* @return {CustomEvent<T>} The new event.
17+
*/
18+
export function createEvent(type, data) {
19+
return new CustomEvent(type, { detail: data });
20+
}

index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ export {
3434
} from './archive/decompress.js';
3535
export { getFullMIMEString, getShortMIMEString } from './codecs/codecs.js';
3636
export { findMimeType } from './file/sniffer.js';
37-
export { GifApplicationExtensionEvent, GifCommentExtensionEvent, GifGraphicControlExtensionEvent,
38-
GifHeaderEvent, GifLogicalScreenEvent, GifParseEventType, GifParser,
39-
GifPlainTextExtensionEvent, GifTableBasedImageEvent } from './image/parsers/gif.js';
37+
export { GifParseEventType, GifParser } from './image/parsers/gif.js';
4038
export { JpegApp0ExtensionEvent, JpegApp0MarkerEvent, JpegApp1ExifEvent, JpegComponentType,
4139
JpegDctType, JpegDefineHuffmanTableEvent, JpegDefineQuantizationTableEvent,
4240
JpegDensityUnits, JpegExtensionThumbnailFormat, JpegHuffmanTableType, JpegParseEventType,

tests/image-parsers-gif.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ describe('bitjs.image.parsers.GifParser', () => {
1515
let trailerFound = false;
1616
let comment;
1717
parser.onLogicalScreen(evt => {
18-
const {logicalScreenWidth, logicalScreenHeight} = evt.logicalScreen;
18+
const {logicalScreenWidth, logicalScreenHeight} = evt.detail;
1919
expect(logicalScreenWidth).equals(32);
2020
expect(logicalScreenHeight).equals(52);
2121
});
2222
parser.onTableBasedImage(evt => {
23-
const {imageWidth, imageHeight} = evt.tableBasedImage;
23+
const {imageWidth, imageHeight} = evt.detail;
2424
expect(imageWidth).equals(32);
2525
expect(imageHeight).equals(52);
2626
});
27-
parser.onCommentExtension(evt => comment = evt.comment);
27+
parser.onCommentExtension(evt => comment = evt.detail);
2828
parser.onTrailer(evt => trailerFound = true);
29-
29+
3030
await parser.start();
3131

3232
expect(trailerFound).equals(true);
@@ -42,10 +42,10 @@ describe('bitjs.image.parsers.GifParser', () => {
4242
let appAuthCode;
4343
let hasAppData = false;
4444
parser.onApplicationExtension(evt => {
45-
appId = evt.applicationExtension.applicationIdentifier
45+
appId = evt.detail.applicationIdentifier
4646
appAuthCode = new TextDecoder().decode(
47-
evt.applicationExtension.applicationAuthenticationCode);
48-
hasAppData = evt.applicationExtension.applicationData.byteLength > 0;
47+
evt.detail.applicationAuthenticationCode);
48+
hasAppData = evt.detail.applicationData.byteLength > 0;
4949
});
5050

5151
await parser.start();

types/image/parsers/exif.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
* @returns {ExifValue}
66
*/
77
export function getExifValue(stream: ByteStream, lookAheadStream: ByteStream, DEBUG?: boolean): ExifValue;
8+
/**
9+
* Reads the entire EXIF profile. The first 2 bytes in the stream must be the TIFF marker (II/MM).
10+
* @param {ByteStream} stream
11+
* @returns {Map<number, ExifValue} A map of all EXIF values found. The key is the EXIF tag number.
12+
*/
13+
export function getExifProfile(stream: ByteStream): Map<number, ExifValue>;
814
export type ExifTagNumber = number;
915
export namespace ExifTagNumber {
1016
const IMAGE_DESCRIPTION: number;

types/image/parsers/exif.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)