UNPKG

@readium/shared

Version:

Shared models to be used across other Readium projects and implementations in Typescript

861 lines (716 loc) 26.3 kB
/* Copyright 2025 Readium Foundation. All rights reserved. * Use of this source code is governed by a BSD-style license, * available in the LICENSE file present in the Github repository of the project. */ /** * Holds the accessibility metadata of a Publication. * * https://www.w3.org/2021/a11y-discov-vocab/latest/ * https://readium.org/webpub-manifest/schema/a11y.schema.json */ export class Accessibility { /** * An established standard to which the described resource conforms. */ public conformsTo: AccessibilityProfile[]; /** * Certification of accessible publications. */ public certification: Certification | null; /** * A human-readable summary of specific accessibility features or deficiencies. */ public summary: string | null; /** * The human sensory perceptual system through which a person may process or perceive information. */ public accessMode: AccessMode[]; /** * A list of single or combined accessModes that are sufficient to understand all the intellectual content. */ public accessModeSufficient: PrimaryAccessMode[]; /** * Content features of the resource. */ public feature: Feature[]; /** * A characteristic of the described resource that is physiologically dangerous to some users. */ public hazard: Hazard[]; /** * Justifications for non-conformance based on exemptions in a given jurisdiction. */ public exemption: Exemption[]; constructor(values: { conformsTo?: AccessibilityProfile[], certification?: Certification | null, summary?: string | null, accessMode?: AccessMode[], accessModeSufficient?: PrimaryAccessMode[], feature?: Feature[], hazard?: Hazard[], exemption?: Exemption[] } = {}) { this.conformsTo = values.conformsTo ?? []; this.certification = values.certification ?? null; this.summary = values.summary ?? null; this.accessMode = values.accessMode ?? []; this.accessModeSufficient = values.accessModeSufficient ?? []; this.feature = values.feature ?? []; this.hazard = values.hazard ?? []; this.exemption = values.exemption ?? []; } /** * Parses an [Accessibility] from its RWPM JSON representation. */ public static deserialize(json: Record<string, any> | string): Accessibility | undefined { if (!json || typeof json !== 'object') return; type AccessibilityJson = { conformsTo?: string[]; certification?: { certifiedBy: string; credential: string; report: string; }; summary?: string; accessMode?: string[]; accessModeSufficient?: string[][]; feature?: string[]; hazard?: string[]; exemption?: string[]; }; const accessibilityJson = json as AccessibilityJson; return new Accessibility({ conformsTo: accessibilityJson.conformsTo ? accessibilityJson.conformsTo.map(uri => AccessibilityProfile.deserialize(uri)) .filter((profile): profile is AccessibilityProfile => profile !== undefined) : undefined, certification: accessibilityJson.certification ? Certification.deserialize(accessibilityJson.certification) : undefined, summary: accessibilityJson.summary, accessMode: accessibilityJson.accessMode ? accessibilityJson.accessMode.map(value => AccessMode.deserialize(value)) .filter((mode): mode is AccessMode => mode !== undefined) : undefined, accessModeSufficient: accessibilityJson.accessModeSufficient ? accessibilityJson.accessModeSufficient.map(modes => PrimaryAccessMode.deserialize(modes)) .filter((mode): mode is PrimaryAccessMode => mode !== undefined) : undefined, feature: accessibilityJson.feature ? accessibilityJson.feature.map(value => Feature.deserialize(value)) .filter((feature): feature is Feature => feature !== undefined) : undefined, hazard: accessibilityJson.hazard ? accessibilityJson.hazard.map(value => Hazard.deserialize(value)) .filter((hazard): hazard is Hazard => hazard !== undefined) : undefined, exemption: accessibilityJson.exemption ? accessibilityJson.exemption.map(value => Exemption.deserialize(value)) .filter((exemption): exemption is Exemption => exemption !== undefined) : undefined }); } /** * Serializes an [Accessibility] to its RWPM JSON representation. */ public serialize(): Record<string, any> { const result: Record<string, any> = {}; if (this.conformsTo?.length > 0) { result.conformsTo = this.conformsTo.map(profile => profile.serialize()); } if (this.certification !== undefined && this.certification !== null) { result.certification = this.certification.serialize(); } if (this.summary !== undefined && this.summary !== null) { result.summary = this.summary; } if (this.accessMode?.length > 0) { result.accessMode = this.accessMode.map(mode => mode.serialize()); } if (this.accessModeSufficient?.length > 0) { result.accessModeSufficient = this.accessModeSufficient.map(mode => mode.serialize()); } if (this.feature?.length > 0) { result.feature = this.feature.map(feature => feature.serialize()); } if (this.hazard?.length > 0) { result.hazard = this.hazard.map(hazard => hazard.serialize()); } if (this.exemption?.length > 0) { result.exemption = this.exemption.map(exemption => exemption.serialize()); } return result; } } export class AccessibilityProfile { public readonly uri: string; constructor(uri: string) { this.uri = uri; } /** * Parses an [AccessibilityProfile] from its RWPM JSON representation. */ public static deserialize(json: any): AccessibilityProfile | undefined { if (!json || typeof json !== 'string') return; return new AccessibilityProfile(json); } /** * Serializes an [AccessibilityProfile] to its RWPM JSON representation. */ public serialize(): any { return this.uri; } /** * EPUB Accessibility 1.0 WCAG 2.0 A – http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a */ public static readonly EPUB_A11Y_10_WCAG_20_A = new AccessibilityProfile('http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a'); /** * EPUB Accessibility 1.0 WCAG 2.0 AA – http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aa */ public static readonly EPUB_A11Y_10_WCAG_20_AA = new AccessibilityProfile('http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aa'); /** * EPUB Accessibility 1.0 WCAG 2.0 AAA – http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aaa */ public static readonly EPUB_A11Y_10_WCAG_20_AAA = new AccessibilityProfile('http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aaa'); /** * EPUB Accessibility 1.1 WCAG 2.0 A – https://www.w3.org/TR/epub-a11y-11#wcag-2.0-a */ public static readonly EPUB_A11Y_11_WCAG_20_A = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.0-a'); /** * EPUB Accessibility 1.1 WCAG 2.0 AA – https://www.w3.org/TR/epub-a11y-11#wcag-2.0-aa */ public static readonly EPUB_A11Y_11_WCAG_20_AA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.0-aa'); /** * EPUB Accessibility 1.1 WCAG 2.0 AAA – https://www.w3.org/TR/epub-a11y-11#wcag-2.0-aaa */ public static readonly EPUB_A11Y_11_WCAG_20_AAA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.0-aaa'); /** * EPUB Accessibility 1.1 WCAG 2.1 A – https://www.w3.org/TR/epub-a11y-11#wcag-2.1-a */ public static readonly EPUB_A11Y_11_WCAG_21_A = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.1-a'); /** * EPUB Accessibility 1.1 WCAG 2.1 AA – https://www.w3.org/TR/epub-a11y-11#wcag-2.1-aa */ public static readonly EPUB_A11Y_11_WCAG_21_AA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.1-aa'); /** * EPUB Accessibility 1.1 WCAG 2.1 AAA – https://www.w3.org/TR/epub-a11y-11#wcag-2.1-aaa */ public static readonly EPUB_A11Y_11_WCAG_21_AAA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.1-aaa'); /** * EPUB Accessibility 1.1 WCAG 2.2 A – https://www.w3.org/TR/epub-a11y-11#wcag-2.2-a */ public static readonly EPUB_A11Y_11_WCAG_22_A = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.2-a'); /** * EPUB Accessibility 1.1 WCAG 2.2 AA – https://www.w3.org/TR/epub-a11y-11#wcag-2.2-aa */ public static readonly EPUB_A11Y_11_WCAG_22_AA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.2-aa'); /** * EPUB Accessibility 1.1 WCAG 2.2 AAA – https://www.w3.org/TR/epub-a11y-11#wcag-2.2-aaa */ public static readonly EPUB_A11Y_11_WCAG_22_AAA = new AccessibilityProfile('https://www.w3.org/TR/epub-a11y-11#wcag-2.2-aaa'); /** * Returns true if the profile is a WCAG Level A profile. */ public get isWCAGLevelA(): boolean { return this === AccessibilityProfile.EPUB_A11Y_10_WCAG_20_A || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_20_A || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_21_A || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_22_A; } /** * Returns true if the profile is a WCAG Level AA profile. */ public get isWCAGLevelAA(): boolean { return this === AccessibilityProfile.EPUB_A11Y_10_WCAG_20_AA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_20_AA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_21_AA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_22_AA; } /** * Returns true if the profile is a WCAG Level AAA profile. */ public get isWCAGLevelAAA(): boolean { return this === AccessibilityProfile.EPUB_A11Y_10_WCAG_20_AAA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_20_AAA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_21_AAA || this === AccessibilityProfile.EPUB_A11Y_11_WCAG_22_AAA; } } export class Certification { public readonly certifiedBy: string | null; public readonly credential: string | null; public readonly report: string | null; constructor( certifiedBy: string | null = null, credential: string | null = null, report: string | null = null ) { this.certifiedBy = certifiedBy; this.credential = credential; this.report = report; } /** * Parses a [Certification] from its RWPM JSON representation. */ public static deserialize(json: any): Certification | undefined { if (!json || typeof json !== 'object') return; return new Certification( json.certifiedBy, json.credential, json.report ); } /** * Serializes a [Certification] to its RWPM JSON representation. */ public serialize(): Record<string, any> { const json: any = {}; if (this.certifiedBy) { json.certifiedBy = this.certifiedBy; } if (this.credential) { json.credential = this.credential; } if (this.report) { json.report = this.report; } return json; } } export class AccessMode { public readonly value: string; constructor(value: string) { this.value = value; } /** * Parses an [AccessMode] from its RWPM JSON representation. */ public static deserialize(json: any): AccessMode | undefined { if (!json || typeof json !== 'string') return; return new AccessMode(json); } /** * Serializes an [AccessMode] to its RWPM JSON representation. */ public serialize(): any { return this.value; } /** * Access mode for auditory content. */ public static readonly AUDITORY = new AccessMode('auditory'); /** * Access mode for chart on visual content. */ public static readonly CHART_ON_VISUAL = new AccessMode('chartOnVisual'); /** * Access mode for chemical on visual content. */ public static readonly CHEM_ON_VISUAL = new AccessMode('chemOnVisual'); /** * Access mode for color dependent content. */ public static readonly COLOR_DEPENDENT = new AccessMode('colorDependent'); /** * Access mode for diagram on visual content. */ public static readonly DIAGRAM_ON_VISUAL = new AccessMode('diagramOnVisual'); /** * Access mode for math on visual content. */ public static readonly MATH_ON_VISUAL = new AccessMode('mathOnVisual'); /** * Access mode for music on visual content. */ public static readonly MUSIC_ON_VISUAL = new AccessMode('musicOnVisual'); /** * Access mode for tactile content. */ public static readonly TACTILE = new AccessMode('tactile'); /** * Access mode for text on visual content. */ public static readonly TEXT_ON_VISUAL = new AccessMode('textOnVisual'); /** * Access mode for textual content. */ public static readonly TEXTUAL = new AccessMode('textual'); /** * Access mode for visual content. */ public static readonly VISUAL = new AccessMode('visual'); } export class PrimaryAccessMode { public readonly value!: string | string[]; private static readonly VALID_MODES = new Set(['auditory', 'tactile', 'textual', 'visual']); constructor(value: string | string[]) { if (typeof value === 'string') { if (!PrimaryAccessMode.VALID_MODES.has(value.toLowerCase())) { return; } this.value = value.toLowerCase(); } else { // Filter out invalid modes and duplicates const validModes = value.filter(mode => PrimaryAccessMode.VALID_MODES.has(mode.toLowerCase()) ); if (validModes.length === 0) { return; } this.value = Array.from(new Set(validModes)); } } /** * Parses a [PrimaryAccessMode] from its RWPM JSON representation. */ public static deserialize(json: any): PrimaryAccessMode | undefined { if (!json) return; if (typeof json === 'string') { return new PrimaryAccessMode(json); } if (!Array.isArray(json)) return undefined; // Create a new array with only valid modes const validModes = json.filter(mode => { if (!mode) { return false; } return PrimaryAccessMode.VALID_MODES.has(mode.toLowerCase()); }); if (validModes.length === 0) { return undefined; } return new PrimaryAccessMode(validModes); } /** * Serializes a [PrimaryAccessMode] to its RWPM JSON representation. */ public serialize(): string | string[] { return this.value; } /** * Primary access mode for auditory content. */ public static readonly AUDITORY = new PrimaryAccessMode('auditory'); /** * Primary access mode for tactile content. */ public static readonly TACTILE = new PrimaryAccessMode('tactile'); /** * Primary access mode for textual content. */ public static readonly TEXTUAL = new PrimaryAccessMode('textual'); /** * Primary access mode for visual content. */ public static readonly VISUAL = new PrimaryAccessMode('visual'); } export class Feature { public readonly value: string; constructor(value: string) { this.value = value; } /** * Parses a [Feature] from its RWPM JSON representation. */ public static deserialize(json: any): Feature | undefined { if (!json || typeof json !== 'string') return; return new Feature(json); } /** * Serializes a [Feature] to its RWPM JSON representation. */ public serialize(): any { return this.value; } /** * Feature for no accessibility features. */ public static readonly NONE = new Feature('none'); /** * Feature for annotations. */ public static readonly ANNOTATIONS = new Feature('annotations'); /** * Feature for ARIA. */ public static readonly ARIA = new Feature('ARIA'); /** * Feature for index. */ public static readonly INDEX = new Feature('index'); /** * Feature for page break markers. */ public static readonly PAGE_BREAK_MARKERS = new Feature('pageBreakMarkers'); /** * Feature for page navigation. */ public static readonly PAGE_NAVIGATION = new Feature('pageNavigation'); /** * Feature for print page numbers. */ public static readonly PRINT_PAGE_NUMBERS = new Feature('printPageNumbers'); /** * Feature for reading order. */ public static readonly READING_ORDER = new Feature('readingOrder'); /** * Feature for structural navigation. */ public static readonly STRUCTURAL_NAVIGATION = new Feature('structuralNavigation'); /** * Feature for table of contents. */ public static readonly TABLE_OF_CONTENTS = new Feature('tableOfContents'); /** * Feature for tagged PDF. */ public static readonly TAGGED_PDF = new Feature('taggedPDF'); /** * Feature for alternative text. */ public static readonly ALTERNATIVE_TEXT = new Feature('alternativeText'); /** * Feature for audio description. */ public static readonly AUDIO_DESCRIPTION = new Feature('audioDescription'); /** * Feature for captions. */ public static readonly CAPTIONS = new Feature('captions'); /** * Feature for closed captions. */ public static readonly CLOSED_CAPTIONS = new Feature('closedCaptions'); /** * Feature for described math. */ public static readonly DESCRIBED_MATH = new Feature('describedMath'); /** * Feature for long description. */ public static readonly LONG_DESCRIPTION = new Feature('longDescription'); /** * Feature for open captions. */ public static readonly OPEN_CAPTIONS = new Feature('openCaptions'); /** * Feature for sign language. */ public static readonly SIGN_LANGUAGE = new Feature('signLanguage'); /** * Feature for transcript. */ public static readonly TRANSCRIPT = new Feature('transcript'); /** * Feature for display transformability. */ public static readonly DISPLAY_TRANSFORMABILITY = new Feature('displayTransformability'); /** * Feature for synchronized audio text. */ public static readonly SYNCHRONIZED_AUDIO_TEXT = new Feature('synchronizedAudioText'); /** * Feature for timing control. */ public static readonly TIMING_CONTROL = new Feature('timingControl'); /** * Feature for unlocked. */ public static readonly UNLOCKED = new Feature('unlocked'); /** * Feature for ChemML. */ public static readonly CHEM_ML = new Feature('ChemML'); /** * Feature for LaTeX. */ public static readonly LATEX = new Feature('latex'); /** * Feature for LaTeX chemistry. */ public static readonly LATEX_CHEMISTRY = new Feature('latex-chemistry'); /** * Feature for MathML. */ public static readonly MATH_ML = new Feature('MathML'); /** * Feature for MathML chemistry. */ public static readonly MATH_ML_CHEMISTRY = new Feature('MathML-chemistry'); /** * Feature for TTS markup. */ public static readonly TTS_MARKUP = new Feature('ttsMarkup'); /** * Feature for high contrast audio. */ public static readonly HIGH_CONTRAST_AUDIO = new Feature('highContrastAudio'); /** * Feature for high contrast display. */ public static readonly HIGH_CONTRAST_DISPLAY = new Feature('highContrastDisplay'); /** * Feature for large print. */ public static readonly LARGE_PRINT = new Feature('largePrint'); /** * Feature for braille. */ public static readonly BRAILLE = new Feature('braille'); /** * Feature for tactile graphic. */ public static readonly TACTILE_GRAPHIC = new Feature('tactileGraphic'); /** * Feature for tactile object. */ public static readonly TACTILE_OBJECT = new Feature('tactileObject'); /** * Feature for full ruby annotations. */ public static readonly FULL_RUBY_ANNOTATIONS = new Feature('fullRubyAnnotations'); /** * Feature for horizontal writing. */ public static readonly HORIZONTAL_WRITING = new Feature('horizontalWriting'); /** * Feature for ruby annotations. */ public static readonly RUBY_ANNOTATIONS = new Feature('rubyAnnotations'); /** * Feature for vertical writing. */ public static readonly VERTICAL_WRITING = new Feature('verticalWriting'); /** * Feature for additional word segmentation. */ public static readonly WITH_ADDITIONAL_WORD_SEGMENTATION = new Feature('withAdditionalWordSegmentation'); /** * Feature for lack of additional word segmentation. */ public static readonly WITHOUT_ADDITIONAL_WORD_SEGMENTATION = new Feature('withoutAdditionalWordSegmentation'); } export class Hazard { public readonly value: string; constructor(value: string) { this.value = value; } /** * Parses a [Hazard] from its RWPM JSON representation. */ public static deserialize(json: any): Hazard | undefined { if (!json || typeof json !== 'string') return; return new Hazard(json); } /** * Serializes a [Hazard] to its RWPM JSON representation. */ public serialize(): any { return this.value; } /** * Hazard for flashing. */ public static readonly FLASHING = new Hazard('flashing'); /** * Hazard for no flashing hazard. */ public static readonly NO_FLASHING_HAZARD = new Hazard('noFlashingHazard'); /** * Hazard for unknown flashing hazard. */ public static readonly UNKNOWN_FLASHING_HAZARD = new Hazard('unknownFlashingHazard'); /** * Hazard for motion simulation. */ public static readonly MOTION_SIMULATION = new Hazard('motionSimulation'); /** * Hazard for no motion simulation hazard. */ public static readonly NO_MOTION_SIMULATION_HAZARD = new Hazard('noMotionSimulationHazard'); /** * Hazard for unknown motion simulation hazard. */ public static readonly UNKNOWN_MOTION_SIMULATION_HAZARD = new Hazard('unknownMotionSimulationHazard'); /** * Hazard for sound. */ public static readonly SOUND = new Hazard('sound'); /** * Hazard for no sound hazard. */ public static readonly NO_SOUND_HAZARD = new Hazard('noSoundHazard'); /** * Hazard for unknown sound hazard. */ public static readonly UNKNOWN_SOUND_HAZARD = new Hazard('unknownSoundHazard'); /** * Hazard for unknown hazard. */ public static readonly UNKNOWN = new Hazard('unknown'); /** * Hazard for no hazard. */ public static readonly NONE = new Hazard('none'); } export class Exemption { public readonly value: string; constructor(value: string) { this.value = value; } /** * Parses an [Exemption] from its RWPM JSON representation. */ public static deserialize(json: any): Exemption | undefined { if (!json || typeof json !== 'string') return; return new Exemption(json); } /** * Serializes an [Exemption] to its RWPM JSON representation. */ public serialize(): any { return this.value; } /** * None exemption. */ public static readonly NONE = new Exemption('none'); /** * Documented exemption. */ public static readonly DOCUMENTED = new Exemption('documented'); /** * Legal exemption. */ public static readonly LEGAL = new Exemption('legal'); /** * Temporary exemption. */ public static readonly TEMPORARY = new Exemption('temporary'); /** * Technical exemption. */ public static readonly TECHNICAL = new Exemption('technical'); /** * EAA disproportionate burden exemption. */ public static readonly EAA_DISPROPORTIONATE_BURDEN = new Exemption('eaa-disproportionate-burden'); /** * EAA fundamental alteration exemption. */ public static readonly EAA_FUNDAMENTAL_ALTERATION = new Exemption('eaa-fundamental-alteration'); /** * EAA microenterprise exemption. */ public static readonly EAA_MICROENTERPRISE = new Exemption('eaa-microenterprise'); /** * EAA technical impossibility exemption. */ public static readonly EAA_TECHNICAL_IMPOSSIBILITY = new Exemption('eaa-technical-impossibility'); /** * EAA temporary exemption. */ public static readonly EAA_TEMPORARY = new Exemption('eaa-temporary'); }