diff --git a/package-lock.json b/package-lock.json index be702943..38042b02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", + "@types/sax": "^1.2.7", "eslint": "^9.1.1", "filesaver.js-npm": "latest", "globals": "^15.0.0", @@ -580,12 +581,30 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/node": { + "version": "20.14.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz", + "integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -2865,6 +2884,12 @@ "node": "*" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/package.json b/package.json index 591b4d61..da0c1afc 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", + "@types/sax": "^1.2.7", "eslint": "^9.1.1", "filesaver.js-npm": "latest", "globals": "^15.0.0", diff --git a/src/main/js/doc.js b/src/main/js/doc.js index e73b6bdb..f6420062 100644 --- a/src/main/js/doc.js +++ b/src/main/js/doc.js @@ -34,6 +34,14 @@ import { ComputedLength, hasOwnProperty, parseLength } from "./utils.js"; * @module imscDoc */ +/** + * @typedef {import("./error").ErrorHandler} ErrorHandler + */ + +/** + * @typedef {sax.Tag | sax.QualifiedTag} Node + */ + /** * Allows a client to provide callbacks to handle children of the element * @typedef {Object} MetadataHandler @@ -47,7 +55,7 @@ import { ComputedLength, hasOwnProperty, parseLength } from "./utils.js"; * @callback OpenTagCallBack * @param {string} ns Namespace URI of the element * @param {string} name Local name of the element - * @param {Object[]} attributes List of attributes, each consisting of a + * @param {Record} attributes List of attributes, each consisting of a * `uri`, `name` and `value` */ @@ -70,9 +78,9 @@ import { ComputedLength, hasOwnProperty, parseLength } from "./utils.js"; * be called back when nodes are present in elements. * * @param {string} xmlstring XML document - * @param {?module:imscUtils.ErrorHandler} errorHandler Error callback + * @param {ErrorHandler} errorHandler Error callback * @param {?MetadataHandler} metadataHandler Callback for elements - * @returns {Object} Opaque in-memory representation of an IMSC1 document + * @returns {?TT} Opaque in-memory representation of an IMSC1 document */ export function fromXML(xmlstring, errorHandler, metadataHandler) { @@ -807,19 +815,31 @@ function resolveTiming(doc, element, prev_sibling, parent) { } -class ForeignElement { +export class ForeignElement { constructor(node) { this.node = node; } } -class TT { +export class TT { constructor() { + /** + * @type {number[]} + */ this.events = []; this.head = new Head(); + + /** + * @type {?Body} + */ this.body = null; } + /** + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(node, xmllang, errorHandler) { /* compute cell resolution */ @@ -887,7 +907,9 @@ class TT { }; /* xml:lang */ - + /** + * @type {string} + */ this.lang = xmllang; } @@ -922,10 +944,10 @@ class TT { } - /* + /** * Retrieves the range of ISD times covered by the document * - * @returns {Array} Array of two elements: min_begin_time and max_begin_time + * @returns {[number, number]} Array of two elements: min_begin_time and max_begin_time * */ getMediaTimeRange() { @@ -933,10 +955,10 @@ class TT { return [this.events[0], this.events[this.events.length - 1]]; }; - /* + /** * Returns list of ISD begin times * - * @returns {Array} + * @returns {number[]} */ getMediaTimeEvents() { @@ -948,7 +970,7 @@ class TT { * Represents a TTML Head element */ -class Head { +export class Head { constructor() { this.styling = new Styling(); this.layout = new Layout(); @@ -959,9 +981,16 @@ class Head { * Represents a TTML Styling element */ -class Styling { +export class Styling { constructor() { + /** + * @type {Record} + */ this.styles = {}; + + /** + * @type {Record} + */ this.initials = {}; } } @@ -970,13 +999,28 @@ class Styling { * Represents a TTML Style element */ -class Style { +export class Style { constructor() { + /** + * @type {string} + */ this.id = null; + + /** + * @type {Record} + */ this.styleAttrs = null; + + /** + * @type {string[]} + */ this.styleRefs = null; } + /** + * @param {Node} node + * @param {ErrorHandler} errorHandler + */ initFromNode(node, errorHandler) { this.id = elementGetXMLID(node); this.styleAttrs = elementGetStyles(node, errorHandler); @@ -988,11 +1032,17 @@ class Style { * Represents a TTML initial element */ -class Initial { +export class Initial { constructor() { + /** + * @type {Record} + */ this.styleAttrs = null; } + /** + * @param {Node} node + */ initFromNode(node) { this.styleAttrs = {}; @@ -1018,13 +1068,16 @@ class Initial { * */ -class Layout { +export class Layout { constructor() { this.regions = {}; } } -class ContentElement { +export class ContentElement { + /** + * @param {string} kind + */ constructor(kind) { this.kind = kind; } @@ -1034,13 +1087,32 @@ class ContentElement { * Represents a TTML image element */ -class Image extends ContentElement { +export class Image extends ContentElement { + /** + * @param {string} src + * @param {string} type + */ constructor(src, type) { super("image"); + + /** + * @type {string} + */ this.src = src; + + /** + * @type {string} + */ this.type = type; } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, xmllang, errorHandler) { this.src = "src" in node.attributes ? node.attributes.src.value : null; @@ -1068,31 +1140,56 @@ class Image extends ContentElement { * */ -class IdentifiedElement { +export class IdentifiedElement { + /** + * @param {string} id + */ constructor(id) { this.id = id; } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + */ initFromNode(doc, parent, node) { this.id = elementGetXMLID(node); } } -class LayoutElement { +export class LayoutElement { + /** + * @param {string} id + */ constructor(id) { this.regionID = id; } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + */ initFromNode(doc, parent, node) { this.regionID = elementGetRegionID(node); } } -class StyledElement { +export class StyledElement { + /** + * @param {Record} styleAttrs + */ constructor(styleAttrs) { this.styleAttrs = styleAttrs; } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, errorHandler) { this.styleAttrs = elementGetStyles(node, errorHandler); @@ -1104,7 +1201,10 @@ class StyledElement { } } -class AnimatedElement { +export class AnimatedElement { + /** + * @param {any[]} sets + */ constructor(sets) { this.sets = sets; } @@ -1114,7 +1214,10 @@ class AnimatedElement { } } -class ContainerElement { +export class ContainerElement { + /** + * @param {string} contents + */ constructor(contents) { this.contents = contents; } @@ -1124,13 +1227,25 @@ class ContainerElement { } } -class TimedElement { +export class TimedElement { + /** + * + * @param {number} explicit_begin + * @param {number} explicit_end + * @param {number} explicit_dur + */ constructor(explicit_begin, explicit_end, explicit_dur) { this.explicit_begin = explicit_begin; this.explicit_end = explicit_end; this.explicit_dur = explicit_dur; } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, errorHandler) { const t = processTiming(doc, parent, node, errorHandler); this.explicit_begin = t.explicit_begin; @@ -1145,11 +1260,17 @@ class TimedElement { * Represents a TTML body element */ -class Body extends ContentElement { +export class Body extends ContentElement { constructor() { super("body"); } + /** + * @param {TT} doc + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, node, xmllang, errorHandler) { StyledElement.prototype.initFromNode.call(this, doc, null, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, null, node, errorHandler); @@ -1165,11 +1286,18 @@ class Body extends ContentElement { * Represents a TTML div element */ -class Div extends ContentElement { +export class Div extends ContentElement { constructor() { super("div"); } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, xmllang, errorHandler) { StyledElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); @@ -1185,11 +1313,18 @@ class Div extends ContentElement { * Represents a TTML p element */ -class P extends ContentElement { +export class P extends ContentElement { constructor() { super("p"); } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, xmllang, errorHandler) { StyledElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); @@ -1205,11 +1340,19 @@ class P extends ContentElement { * Represents a TTML span element */ -class Span extends ContentElement { +export class Span extends ContentElement { constructor() { super("span"); } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {string} xmllang + * @param {string} xmlspace + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, xmllang, xmlspace, errorHandler) { StyledElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); @@ -1226,11 +1369,19 @@ class Span extends ContentElement { * Represents a TTML anonymous span element */ -class AnonymousSpan extends ContentElement { +export class AnonymousSpan extends ContentElement { constructor() { super("span"); } + /** + * @param {TT} doc + * @param {Node} parent + * @param {string} text + * @param {string} xmlspace + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromText(doc, parent, text, xmllang, xmlspace, errorHandler) { TimedElement.prototype.initFromNode.call(this, doc, parent, null, errorHandler); @@ -1244,11 +1395,18 @@ class AnonymousSpan extends ContentElement { * Represents a TTML br element */ -class Br extends ContentElement { +export class Br extends ContentElement { constructor() { super("br"); } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, xmllang, errorHandler) { LayoutElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); @@ -1262,9 +1420,13 @@ class Br extends ContentElement { * */ -class Region { +export class Region { constructor() { } + /** + * @param {string} xmllang + * @returns {Region} + */ createDefaultRegion(xmllang) { const r = new Region(); @@ -1280,6 +1442,12 @@ class Region { return r; } + /** + * @param {TT} doc + * @param {Node} node + * @param {string} xmllang + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, node, xmllang, errorHandler) { IdentifiedElement.prototype.initFromNode.call(this, doc, null, node, errorHandler); TimedElement.prototype.initFromNode.call(this, doc, null, node, errorHandler); @@ -1304,10 +1472,16 @@ class Region { * */ -class Set { +export class Set { constructor() { } + /** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {ErrorHandler} errorHandler + */ initFromNode(doc, parent, node, errorHandler) { TimedElement.prototype.initFromNode.call(this, doc, parent, node, errorHandler); @@ -1341,14 +1515,27 @@ class Set { * */ +/** + * @param {Node} node + * @returns {string | null} + */ function elementGetXMLID(node) { return node && "xml:id" in node.attributes ? node.attributes["xml:id"].value || null : null; } +/** + * @param {Node} node + * @returns {string} + */ function elementGetRegionID(node) { return node && "region" in node.attributes ? node.attributes.region.value : ""; } +/** + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {string} + */ function elementGetTimeContainer(node, errorHandler) { const tc = node && "timeContainer" in node.attributes ? node.attributes.timeContainer.value : null; @@ -1371,12 +1558,21 @@ function elementGetTimeContainer(node, errorHandler) { } +/** + * @param {Node} node + * @returns {string[]} + */ function elementGetStyleRefs(node) { return node && "style" in node.attributes ? node.attributes.style.value.split(" ") : []; } +/** + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {Record} + */ function elementGetStyles(node, errorHandler) { const s = {}; @@ -1418,6 +1614,12 @@ function elementGetStyles(node, errorHandler) { return s; } +/** + * @param {Node} node + * @param {string} ns + * @param {string} name + * @returns {string | null} + */ function findAttribute(node, ns, name) { for (const i in node.attributes) { @@ -1431,6 +1633,11 @@ function findAttribute(node, ns, name) { return null; } +/** + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {number | null} + */ function extractAspectRatio(node, errorHandler) { let ar = findAttribute(node, ns_ittp, "aspectRatio"); @@ -1475,9 +1682,12 @@ function extractAspectRatio(node, errorHandler) { } -/* +/** * Returns the cellResolution attribute from a node * + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {{w: number, h: number}} */ function extractCellResolution(node, errorHandler) { @@ -1512,6 +1722,11 @@ function extractCellResolution(node, errorHandler) { } +/** + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {{ effectiveFrameRate: number, tickRate: number }} + */ function extractFrameAndTickRate(node, errorHandler) { // subFrameRate is ignored per IMSC1 specification @@ -1604,6 +1819,11 @@ function extractFrameAndTickRate(node, errorHandler) { } +/** + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {{w: number, h: number} | null} + */ function extractExtent(node, errorHandler) { const attr = findAttribute(node, ns_tts, "extent"); @@ -1635,6 +1855,12 @@ function extractExtent(node, errorHandler) { } +/** + * @param {number} tickRate + * @param {number} effectiveFrameRate + * @param {string} str + * @returns {number | null} + */ function parseTimeExpression(tickRate, effectiveFrameRate, str) { const CLOCK_TIME_FRACTION_RE = /^(\d{2,}):(\d\d):(\d\d(?:\.\d+)?)$/; @@ -1700,6 +1926,13 @@ function parseTimeExpression(tickRate, effectiveFrameRate, str) { return r; } +/** + * @param {TT} doc + * @param {Node} parent + * @param {Node} node + * @param {ErrorHandler} errorHandler + * @returns {{explicit_begin: number, explicit_end: number, explicit_dur: number}} + */ function processTiming(doc, parent, node, errorHandler) { /* determine explicit begin */ @@ -1758,6 +1991,11 @@ function processTiming(doc, parent, node, errorHandler) { } +/** + * @param {Styling} styling + * @param {Style} style + * @param {ErrorHandler} errorHandler + */ function mergeChainedStyles(styling, style, errorHandler) { while (style.styleRefs.length > 0) { @@ -1777,6 +2015,12 @@ function mergeChainedStyles(styling, style, errorHandler) { } +/** + * @param {Styling} styling + * @param {string[]} stylerefs + * @param {Record} styleattrs + * @param {ErrorHandler} errorHandler + */ function mergeReferencedStyles(styling, stylerefs, styleattrs, errorHandler) { for (let i = stylerefs.length - 1; i >= 0; i--) { @@ -1794,6 +2038,10 @@ function mergeReferencedStyles(styling, stylerefs, styleattrs, errorHandler) { } +/** + * @param {Record} from_styles + * @param {Record} into_styles + */ function mergeStylesIfNotPresent(from_styles, into_styles) { for (const sname in from_styles) { @@ -1811,13 +2059,15 @@ function mergeStylesIfNotPresent(from_styles, into_styles) { /* TODO: validate style format at parsing */ -/* +/** * Binary search utility function * * @typedef {Object} BinarySearchResult * @property {boolean} found Was an exact match found? * @property {number} index Position of the exact match or insert position * + * @param {number[]} arr + * @param {number} searchval * @returns {BinarySearchResult} */ diff --git a/src/main/js/error.js b/src/main/js/error.js index 84c27f69..fc6b8e3d 100644 --- a/src/main/js/error.js +++ b/src/main/js/error.js @@ -3,6 +3,14 @@ * */ +/** + * @module imscError + */ + +/** + * @param {ErrorHandler} errorHandler + * @param {string} msg + */ export function reportWarning(errorHandler, msg) { if (errorHandler && errorHandler.warn && errorHandler.warn(msg)) @@ -10,6 +18,10 @@ export function reportWarning(errorHandler, msg) { } +/** + * @param {ErrorHandler} errorHandler + * @param {string} msg + */ export function reportError(errorHandler, msg) { if (errorHandler && errorHandler.error && errorHandler.error(msg)) @@ -17,6 +29,10 @@ export function reportError(errorHandler, msg) { } +/** + * @param {ErrorHandler} errorHandler + * @param {string} msg + */ export function reportFatal(errorHandler, msg) { if (errorHandler && errorHandler.fatal) @@ -25,3 +41,20 @@ export function reportFatal(errorHandler, msg) { throw msg; } + +/** + * Generic interface for handling events. The interface exposes four + * methods: + * -
info
: unusual event that does not result in an inconsistent state + * -
warn
: unexpected event that should not result in an inconsistent state + * -
error
: unexpected event that may result in an inconsistent state + * -
fatal
: unexpected event that results in an inconsistent state + * and termination of processing + * Each method takes a single
string
describing the event as argument, + * and returns a single
boolean
, which terminates processing if
true
. + * + * @typedef {Object} ErrorHandler + * @property {(msg: string) => boolean} warn + * @property {(msg: string) => boolean} error + * @property {(msg: string) => boolean} fatal + */ diff --git a/src/main/js/html.js b/src/main/js/html.js index bd0a557a..9a62ac70 100755 --- a/src/main/js/html.js +++ b/src/main/js/html.js @@ -27,6 +27,11 @@ import { reportError } from "./error.js"; import { byName } from "./styles.js"; +/** + * @typedef {import("./isd").ISD} ISD + * @typedef {import("./error").ErrorHandler} ErrorHandler + */ + /** * @module imscHTML */ @@ -36,7 +41,7 @@ const browserIsFirefox = /firefox/i.test(navigator.userAgent); /** * Function that maps
smpte:background
URIs to URLs resolving to image resource * @callback IMGResolver - * @param {string}
smpte:background
URI + * @param {string} value
smpte:background
URI * @return {string} PNG resource URL */ @@ -56,8 +61,8 @@ const browserIsFirefox = /firefox/i.test(navigator.userAgent); * is called for the next ISD, otherwise previousISDState should be set to * null. * - * @param {Object} isd ISD to be rendered - * @param {Object} element Element into which the ISD is rendered + * @param {ISD} isd ISD to be rendered + * @param {HTMLElement} element Element into which the ISD is rendered * @param {?IMGResolver} imgResolver Resolve
smpte:background
URIs into URLs. * @param {?number} eheight Height (in pixel) of the child
div
or null * to use clientHeight of the parent element @@ -65,10 +70,10 @@ const browserIsFirefox = /firefox/i.test(navigator.userAgent); * to use clientWidth of the parent element * @param {?boolean} displayForcedOnlyMode Value of the IMSC1 displayForcedOnlyMode parameter, * or false if null - * @param {?module:imscUtils.ErrorHandler} errorHandler Error callback - * @param {Object} previousISDState State saved during processing of the previous ISD, or null if initial call + * @param {?ErrorHandler} errorHandler Error callback + * @param {ISD} previousISDState State saved during processing of the previous ISD, or null if initial call * @param {?boolean} enableRollUp Enables roll-up animations (see CEA 708) - * @return {Object} ISD state to be provided when this funtion is called for the next ISD + * @return {ISD} ISD state to be provided when this function is called for the next ISD */ export function renderHTML(isd, diff --git a/src/main/js/isd.js b/src/main/js/isd.js index b3880e06..f3e677ad 100644 --- a/src/main/js/isd.js +++ b/src/main/js/isd.js @@ -32,15 +32,20 @@ import { ComputedLength, hasOwnProperty } from "./utils.js"; * @module imscISD */ +/** + * @typedef {import("./doc").TT} TT + * @typedef {import("./error").ErrorHandler} ErrorHandler + */ + /** * Creates a canonical representation of an IMSC1 document returned by
imscDoc.fromXML()
* at a given absolute offset in seconds. This offset does not have to be one of the values returned * by
getMediaTimeEvents()
. * - * @param {Object} tt IMSC1 document + * @param {TT} tt IMSC1 document * @param {number} offset Absolute offset (in seconds) - * @param {?module:imscUtils.ErrorHandler} errorHandler Error callback - * @returns {Object} Opaque in-memory representation of an ISD + * @param {ErrorHandler} errorHandler Error callback + * @returns {ISD} Opaque in-memory representation of an ISD */ export function generateISD(tt, offset, errorHandler) { @@ -720,15 +725,26 @@ function pruneEmptySpans(element) { } } -class ISD { +export class ISD { constructor(tt) { + /** + * @type {ISDContentElement[]} + */ this.contents = []; + + /** + * @type {string} + */ this.aspectRatio = tt.aspectRatio; + + /** + * @type {string} + */ this.lang = tt.lang; } } -class ISDContentElement { +export class ISDContentElement { constructor(ttelem) { /* assume the element is a region if it does not have a kind */ diff --git a/src/main/js/styles.js b/src/main/js/styles.js index df523e96..68423ed2 100644 --- a/src/main/js/styles.js +++ b/src/main/js/styles.js @@ -31,7 +31,22 @@ import { ComputedLength, parseColor, parseLength, parsePosition, parseTextShadow * @module imscStyles */ -class StylingAttributeDefinition { +/** + * @typedef {import("./doc").TT} TT + * @typedef {import("./doc").Node} Node + */ + +export class StylingAttributeDefinition { + /** + * @param {string} ns + * @param {string} name + * @param {string} initialValue + * @param {string[]} appliesTo + * @param {boolean} isInherit + * @param {boolean} isAnimatable + * @param {(value: string) => any} parseFunc + * @param {(doc: TT, parent: Node, element: Node, attr: string) => any} computeFunc + */ constructor(ns, name, initialValue, appliesTo, isInherit, isAnimatable, parseFunc, computeFunc) { this.name = name; this.ns = ns; @@ -1193,12 +1208,18 @@ export const all = [ /* TODO: allow null parse function */ +/** + * @type {Record} + */ export const byQName = {}; for (const i in all) { byQName[all[i].qname] = all[i]; } +/** + * @type {Record} + */ export const byName = {}; for (const j in all) { diff --git a/src/main/js/utils.js b/src/main/js/utils.js index 5a682f5a..036d74e5 100644 --- a/src/main/js/utils.js +++ b/src/main/js/utils.js @@ -28,23 +28,6 @@ * @module imscUtils */ -/* Documents the error handler interface */ - -/** - * @classdesc Generic interface for handling events. The interface exposes four - * methods: - * *
info
: unusual event that does not result in an inconsistent state - * *
warn
: unexpected event that should not result in an inconsistent state - * *
error
: unexpected event that may result in an inconsistent state - * *
fatal
: unexpected event that results in an inconsistent state - * and termination of processing - * Each method takes a single
string
describing the event as argument, - * and returns a single
boolean
, which terminates processing if
true
. - * - * @name ErrorHandler - * @class - */ - /* * Parses a TTML color expression * @@ -75,6 +58,10 @@ const NAMED_COLOR = { cyan: [0, 255, 255, 255], }; +/** + * @param {string} str + * @returns {[number, number, number, number]} + */ export function parseColor(str) { let m; @@ -115,6 +102,10 @@ export function parseColor(str) { const LENGTH_RE = /^((?:\+|-)?\d*(?:\.\d+)?)(px|em|c|%|rh|rw)$/; +/** + * @param {string} str + * @returns {{ value: number, unit: string } | null} + */ export function parseLength(str) { let m; @@ -129,6 +120,10 @@ export function parseLength(str) { return r; }; +/** + * @param {string} str + * @returns {string | any[] | null} + */ export function parseTextShadow(str) { const shadows = str.match(/([^(,)]|\([^)]+\))+/g); @@ -202,6 +197,15 @@ export function parseTextShadow(str) { return r; }; +/** + * @typedef {{ value: number, unit: string }} Offset + * @typedef {{ edge: string, offset: Offset }} Position + */ + +/** + * @param {string} str + * @returns {{h: Position, v: Position} | null} + */ export function parsePosition(str) { /* see https://www.w3.org/TR/ttml2/#style-value-position */ @@ -327,15 +331,27 @@ export function parsePosition(str) { }; export class ComputedLength { + /** + * @param {number} rw + * @param {number} rh + */ constructor(rw, rh) { this.rw = rw; this.rh = rh; } + /** + * @param {number} width + * @param {number} height + * @returns {number} + */ toUsedLength(width, height) { return width * this.rw + height * this.rh; }; + /** + * @returns {boolean} + */ isZero() { return this.rw === 0 && this.rh === 0; }; @@ -406,6 +422,11 @@ export function toComputedLength(lengthVal, lengthUnit, emLength, percentLength, } +/** + * @param {Object} obj + * @param {string} prop + * @returns {boolean} + */ export function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }