UNPKG

@readium/shared

Version:

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

1,055 lines (961 loc) 39.3 kB
import { Accessibility, Feature, AccessMode, PrimaryAccessMode, Hazard } from './Accessibility'; import { Publication } from '../Publication'; import { AccessibilityProfile } from './Accessibility'; import { Localization } from './Localization'; import { Layout } from '../Layout'; /** * Represents a single accessibility claim */ export interface AccessibilityDisplayStatement { id: string; compactString?: string; descriptiveString?: string; } /** * Represents a collection of related accessibility claims which should be * displayed together in a section */ export interface AccessibilityDisplayField { // Unique identifier for this display field id: string; // Title for this display field title: string; // List of accessibility claims to display for this field statements: AccessibilityDisplayStatement[]; // Indicates whether this display field should be rendered shouldDisplay: boolean; } /** * Represents the different ways visual adjustments can be made */ export enum VisualAdjustments { Unknown = 'unknown', Modifiable = 'modifiable', Unmodifiable = 'unmodifiable' } /** * Represents the different types of non-visual reading */ export enum NonvisualReading { NoMetadata = 'noMetadata', Readable = 'readable', NotFully = 'notFully', Unreadable = 'unreadable' } /** * Represents the different types of prerecorded audio */ export enum PrerecordedAudio { NoMetadata = 'noMetadata', Synchronized = 'synchronized', AudioOnly = 'audioOnly', AudioComplementary = 'audioComplementary' } /** * The ways of reading display field is a banner heading that groups * together the following information about how the content facilitates * access. */ export class WaysOfReading implements AccessibilityDisplayField { public readonly id = "ways-of-reading.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly visualAdjustments: VisualAdjustments; public readonly nonvisualReading: NonvisualReading; public readonly nonvisualReadingAltText: boolean; public readonly prerecordedAudio: PrerecordedAudio; public readonly statements: AccessibilityDisplayStatement[]; private constructor( visualAdjustments: VisualAdjustments = VisualAdjustments.Unknown, nonvisualReading: NonvisualReading = NonvisualReading.NoMetadata, nonvisualReadingAltText: boolean = false, prerecordedAudio: PrerecordedAudio = PrerecordedAudio.NoMetadata ) { this.visualAdjustments = visualAdjustments; this.nonvisualReading = nonvisualReading; this.nonvisualReadingAltText = nonvisualReadingAltText; this.prerecordedAudio = prerecordedAudio; // This should be displayed even if there is no metadata this.shouldDisplay = true; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.statements = []; // Visual Adjustments const visualAdjustmentsKey = visualAdjustments === VisualAdjustments.Modifiable ? "ways-of-reading.visual-adjustments.modifiable" : visualAdjustments === VisualAdjustments.Unmodifiable ? "ways-of-reading.visual-adjustments.unmodifiable" : "ways-of-reading.visual-adjustments.unknown"; const visualAdjustmentsLocale = Localization.getString(visualAdjustmentsKey); this.statements.push({ id: visualAdjustmentsKey, compactString: visualAdjustmentsLocale.compact, descriptiveString: visualAdjustmentsLocale.descriptive }); // Non-visual Reading let nonvisualReadingKey = ""; if (nonvisualReading === NonvisualReading.Readable) { nonvisualReadingKey = "ways-of-reading.nonvisual-reading.readable"; } else if (nonvisualReading === NonvisualReading.NotFully) { nonvisualReadingKey = "ways-of-reading.nonvisual-reading.not-fully"; } else if (nonvisualReading === NonvisualReading.Unreadable) { nonvisualReadingKey = "ways-of-reading.nonvisual-reading.none"; } else if (nonvisualReading === NonvisualReading.NoMetadata) { nonvisualReadingKey = "ways-of-reading.nonvisual-reading.no-metadata"; } const nonvisualReadingLocale = Localization.getString(nonvisualReadingKey); this.statements.push({ id: nonvisualReadingKey, compactString: nonvisualReadingLocale.compact, descriptiveString: nonvisualReadingLocale.descriptive }); // Non-visual Reading Alt Text if (nonvisualReadingAltText) { const altTextLocale = Localization.getString("ways-of-reading.nonvisual-reading.alt-text"); this.statements.push({ id: "ways-of-reading.nonvisual-reading.alt-text", compactString: altTextLocale.compact, descriptiveString: altTextLocale.descriptive }); } // Prerecorded Audio let prerecordedAudioKey = ""; if (prerecordedAudio === PrerecordedAudio.Synchronized) { prerecordedAudioKey = "ways-of-reading.prerecorded-audio.synchronized"; } else if (prerecordedAudio === PrerecordedAudio.AudioOnly) { prerecordedAudioKey = "ways-of-reading.prerecorded-audio.only"; } else if (prerecordedAudio === PrerecordedAudio.AudioComplementary) { prerecordedAudioKey = "ways-of-reading.prerecorded-audio.complementary"; } else if (prerecordedAudio === PrerecordedAudio.NoMetadata) { prerecordedAudioKey = "ways-of-reading.prerecorded-audio.no-metadata"; } const prerecordedAudioLocale = Localization.getString(prerecordedAudioKey); this.statements.push({ id: prerecordedAudioKey, compactString: prerecordedAudioLocale.compact, descriptiveString: prerecordedAudioLocale.descriptive }); } public static fromPublication(publication: Publication): WaysOfReading { const a11y = publication.metadata.accessibility ?? new Accessibility(); const features = a11y.feature ?? []; const isFXL = publication.metadata.layout === Layout.fixed; const visualAdjustments = features.some(f => f.value === Feature.DISPLAY_TRANSFORMABILITY.value) ? VisualAdjustments.Modifiable : isFXL ? VisualAdjustments.Unmodifiable : VisualAdjustments.Unknown; const accessModes = a11y.accessMode ?? []; const accessModeSufficient = a11y.accessModeSufficient ?? []; const allText = accessModes.length > 0 && accessModes.every((m: AccessMode) => m.value === AccessMode.TEXTUAL.value) || accessModeSufficient.some((modes: PrimaryAccessMode) => { const modeValue = modes.value; if (Array.isArray(modeValue)) { return modeValue.every(mode => mode === AccessMode.TEXTUAL.value); } return modeValue === AccessMode.TEXTUAL.value; }); const someText = accessModes.some((m: AccessMode) => m.value === AccessMode.TEXTUAL.value) || accessModeSufficient.some((modes: PrimaryAccessMode) => { const modeValue = modes.value; if (Array.isArray(modeValue)) { return modeValue.some(mode => mode === AccessMode.TEXTUAL.value); } return modeValue === AccessMode.TEXTUAL.value; }); const noText = !(accessModes.length === 0 && accessModeSufficient.length === 0) && !accessModes.some((m: AccessMode) => m.value === AccessMode.TEXTUAL.value) && !accessModeSufficient.some((modes: PrimaryAccessMode) => { const modeValue = modes.value; if (Array.isArray(modeValue)) { return modeValue.some(mode => mode === AccessMode.TEXTUAL.value); } return modeValue === AccessMode.TEXTUAL.value; }); const hasTextAlt = features.some(f => [ Feature.LONG_DESCRIPTION.value, Feature.ALTERNATIVE_TEXT.value, Feature.DESCRIBED_MATH.value, Feature.TRANSCRIPT.value ].includes(f.value)); const nonvisualReading = allText ? NonvisualReading.Readable : (someText || hasTextAlt) ? NonvisualReading.NotFully : noText ? NonvisualReading.Unreadable : NonvisualReading.NoMetadata; const nonvisualReadingAltText = hasTextAlt; const prerecordedAudio = features.some(f => f.value === Feature.SYNCHRONIZED_AUDIO_TEXT.value) ? PrerecordedAudio.Synchronized : accessModeSufficient.some((modes: PrimaryAccessMode) => { const modeValue = modes.value; if (Array.isArray(modeValue)) { return modeValue.some(mode => mode === AccessMode.AUDITORY.value); } return modeValue === AccessMode.AUDITORY.value; }) ? PrerecordedAudio.AudioOnly : accessModes.some((m: AccessMode) => m.value === AccessMode.AUDITORY.value) ? PrerecordedAudio.AudioComplementary : PrerecordedAudio.NoMetadata; return new WaysOfReading( visualAdjustments, nonvisualReading, nonvisualReadingAltText, prerecordedAudio ); } } /** * Navigation features of the content */ export class Navigation implements AccessibilityDisplayField { public readonly id = "navigation.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly noMetadata: boolean; public readonly tableOfContents: boolean; public readonly index: boolean; public readonly headings: boolean; public readonly page: boolean; public readonly statements: AccessibilityDisplayStatement[]; private constructor( tableOfContents: boolean = false, index: boolean = false, headings: boolean = false, page: boolean = false ) { this.tableOfContents = tableOfContents; this.index = index; this.headings = headings; this.page = page; this.noMetadata = !tableOfContents && !index && !headings && !page; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.shouldDisplay = !this.noMetadata; this.statements = []; if (tableOfContents) { const tocLocale = Localization.getString("navigation.toc"); this.statements.push({ id: "navigation.toc", compactString: tocLocale.compact, descriptiveString: tocLocale.descriptive }); } if (index) { const indexLocale = Localization.getString("navigation.index"); this.statements.push({ id: "navigation.index", compactString: indexLocale.compact, descriptiveString: indexLocale.descriptive }); } if (headings) { const headingsLocale = Localization.getString("navigation.structural"); this.statements.push({ id: "navigation.structural", compactString: headingsLocale.compact, descriptiveString: headingsLocale.descriptive }); } if (page) { const pageNavLocale = Localization.getString("navigation.page-navigation"); this.statements.push({ id: "navigation.page-navigation", compactString: pageNavLocale.compact, descriptiveString: pageNavLocale.descriptive }); } if (this.statements.length === 0) { const noNavLocale = Localization.getString("navigation.no-metadata"); this.statements.push({ id: "navigation.no-metadata", compactString: noNavLocale.compact, descriptiveString: noNavLocale.descriptive }); } } public static fromPublication(publication: Publication): Navigation { const a11y = publication.metadata.accessibility ?? new Accessibility(); const features = a11y.feature ?? []; return new Navigation( features.some(f => f.value === Feature.TABLE_OF_CONTENTS.value), features.some(f => f.value === Feature.INDEX.value), features.some(f => f.value === Feature.STRUCTURAL_NAVIGATION.value), features.some(f => f.value === Feature.PAGE_NAVIGATION.value) ); } } /** * Represents the different types of rich content */ export enum RichContentType { Math = 'math', Chemistry = 'chemistry', Music = 'music', Diagram = 'diagram', Chart = 'chart', Graph = 'graph', Table = 'table', Image = 'image' } /** * Rich content features of the content */ export class RichContent implements AccessibilityDisplayField { public readonly id = "rich-content.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly noMetadata: boolean; public readonly extendedAltTextDescriptions: boolean; public readonly mathFormula: boolean; public readonly mathFormulaAsMathML: boolean; public readonly mathFormulaAsLaTeX: boolean; public readonly chemicalFormulaAsMathML: boolean; public readonly chemicalFormulaAsLaTeX: boolean; public readonly closedCaptions: boolean; public readonly openCaptions: boolean; public readonly transcript: boolean; public readonly statements: AccessibilityDisplayStatement[]; private constructor( extendedAltTextDescriptions: boolean = false, mathFormula: boolean = false, mathFormulaAsMathML: boolean = false, mathFormulaAsLaTeX: boolean = false, chemicalFormulaAsMathML: boolean = false, chemicalFormulaAsLaTeX: boolean = false, closedCaptions: boolean = false, openCaptions: boolean = false, transcript: boolean = false ) { this.extendedAltTextDescriptions = extendedAltTextDescriptions; this.mathFormula = mathFormula; this.mathFormulaAsMathML = mathFormulaAsMathML; this.mathFormulaAsLaTeX = mathFormulaAsLaTeX; this.chemicalFormulaAsMathML = chemicalFormulaAsMathML; this.chemicalFormulaAsLaTeX = chemicalFormulaAsLaTeX; this.closedCaptions = closedCaptions; this.openCaptions = openCaptions; this.transcript = transcript; this.noMetadata = !extendedAltTextDescriptions && !mathFormula && !mathFormulaAsMathML && !mathFormulaAsLaTeX && !chemicalFormulaAsMathML && !chemicalFormulaAsLaTeX && !closedCaptions && !openCaptions && !transcript; this.shouldDisplay = !this.noMetadata; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.statements = []; if (extendedAltTextDescriptions) { const extendedLocale = Localization.getString("rich-content.extended-descriptions"); this.statements.push({ id: "rich-content.extended-descriptions", compactString: extendedLocale.compact, descriptiveString: extendedLocale.descriptive }); } if (mathFormula) { const mathLocale = Localization.getString("rich-content.accessible-math-described"); this.statements.push({ id: "rich-content.accessible-math-described", compactString: mathLocale.compact, descriptiveString: mathLocale.descriptive }); } if (mathFormulaAsMathML) { const mathMLLocale = Localization.getString("rich-content.math-as-mathml"); this.statements.push({ id: "rich-content.math-as-mathml", compactString: mathMLLocale.compact, descriptiveString: mathMLLocale.descriptive }); } if (mathFormulaAsLaTeX) { const latexLocale = Localization.getString("rich-content.accessible-math-as-latex"); this.statements.push({ id: "rich-content.accessible-math-as-latex", compactString: latexLocale.compact, descriptiveString: latexLocale.descriptive }); } if (chemicalFormulaAsMathML) { const chemMLLocale = Localization.getString("rich-content.accessible-chemistry-as-mathml"); this.statements.push({ id: "rich-content.accessible-chemistry-as-mathml", compactString: chemMLLocale.compact, descriptiveString: chemMLLocale.descriptive }); } if (chemicalFormulaAsLaTeX) { const chemLatexLocale = Localization.getString("rich-content.accessible-chemistry-as-latex"); this.statements.push({ id: "rich-content.accessible-chemistry-as-latex", compactString: chemLatexLocale.compact, descriptiveString: chemLatexLocale.descriptive }); } if (closedCaptions) { const ccLocale = Localization.getString("rich-content.closed-captions"); this.statements.push({ id: "rich-content.closed-captions", compactString: ccLocale.compact, descriptiveString: ccLocale.descriptive }); } if (openCaptions) { const ocLocale = Localization.getString("rich-content.open-captions"); this.statements.push({ id: "rich-content.open-captions", compactString: ocLocale.compact, descriptiveString: ocLocale.descriptive }); } if (transcript) { const transcriptLocale = Localization.getString("rich-content.transcript"); this.statements.push({ id: "rich-content.transcript", compactString: transcriptLocale.compact, descriptiveString: transcriptLocale.descriptive }); } if (this.statements.length === 0) { const unknownLocale = Localization.getString("rich-content.unknown"); this.statements.push({ id: "rich-content.unknown", compactString: unknownLocale.compact, descriptiveString: unknownLocale.descriptive }); } } public static fromPublication(publication: Publication): RichContent { const a11y = publication.metadata.accessibility ?? new Accessibility(); const features = a11y.feature ?? []; return new RichContent( features.some(f => f.value === Feature.LONG_DESCRIPTION.value), features.some(f => f.value === Feature.DESCRIBED_MATH.value), features.some(f => f.value === Feature.MATH_ML.value), features.some(f => f.value === Feature.LATEX.value), features.some(f => f.value === Feature.MATH_ML_CHEMISTRY.value), features.some(f => f.value === Feature.LATEX_CHEMISTRY.value), features.some(f => f.value === Feature.CLOSED_CAPTIONS.value), features.some(f => f.value === Feature.OPEN_CAPTIONS.value), features.some(f => f.value === Feature.TRANSCRIPT.value) ); } } /** * Represents additional accessibility information */ export class AdditionalInformation implements AccessibilityDisplayField { public readonly id = "additional-accessibility-information.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly noMetadata: boolean; public readonly pageBreakMarkers: boolean; public readonly aria: boolean; public readonly audioDescriptions: boolean; public readonly braille: boolean; public readonly rubyAnnotations: boolean; public readonly fullRubyAnnotations: boolean; public readonly highAudioContrast: boolean; public readonly highDisplayContrast: boolean; public readonly largePrint: boolean; public readonly signLanguage: boolean; public readonly tactileGraphics: boolean; public readonly tactileObjects: boolean; public readonly textToSpeechHinting: boolean; public readonly statements: AccessibilityDisplayStatement[]; private constructor( pageBreakMarkers: boolean = false, aria: boolean = false, audioDescriptions: boolean = false, braille: boolean = false, rubyAnnotations: boolean = false, fullRubyAnnotations: boolean = false, highAudioContrast: boolean = false, highDisplayContrast: boolean = false, largePrint: boolean = false, signLanguage: boolean = false, tactileGraphics: boolean = false, tactileObjects: boolean = false, textToSpeechHinting: boolean = false ) { this.pageBreakMarkers = pageBreakMarkers; this.aria = aria; this.audioDescriptions = audioDescriptions; this.braille = braille; this.rubyAnnotations = rubyAnnotations; this.fullRubyAnnotations = fullRubyAnnotations; this.highAudioContrast = highAudioContrast; this.highDisplayContrast = highDisplayContrast; this.largePrint = largePrint; this.signLanguage = signLanguage; this.tactileGraphics = tactileGraphics; this.tactileObjects = tactileObjects; this.textToSpeechHinting = textToSpeechHinting; this.noMetadata = !pageBreakMarkers && !aria && !audioDescriptions && !braille && !rubyAnnotations && !fullRubyAnnotations && !highAudioContrast && !highDisplayContrast && !largePrint && !signLanguage && !tactileGraphics && !tactileObjects && !textToSpeechHinting; this.shouldDisplay = !this.noMetadata; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.statements = []; if (pageBreakMarkers) { const pageBreaksLocale = Localization.getString("additional-accessibility-information.page-breaks"); this.statements.push({ id: "additional-accessibility-information.page-breaks", compactString: pageBreaksLocale.compact, descriptiveString: pageBreaksLocale.descriptive }); } if (aria) { const ariaLocale = Localization.getString("additional-accessibility-information.aria"); this.statements.push({ id: "additional-accessibility-information.aria", compactString: ariaLocale.compact, descriptiveString: ariaLocale.descriptive }); } if (audioDescriptions) { const audioDescLocale = Localization.getString("additional-accessibility-information.audio-descriptions"); this.statements.push({ id: "additional-accessibility-information.audio-descriptions", compactString: audioDescLocale.compact, descriptiveString: audioDescLocale.descriptive }); } if (braille) { const brailleLocale = Localization.getString("additional-accessibility-information.braille"); this.statements.push({ id: "additional-accessibility-information.braille", compactString: brailleLocale.compact, descriptiveString: brailleLocale.descriptive }); } if (rubyAnnotations) { const rubyLocale = Localization.getString("additional-accessibility-information.ruby-annotations"); this.statements.push({ id: "additional-accessibility-information.ruby-annotations", compactString: rubyLocale.compact, descriptiveString: rubyLocale.descriptive }); } if (fullRubyAnnotations) { const fullRubyLocale = Localization.getString("additional-accessibility-information.full-ruby-annotations"); this.statements.push({ id: "additional-accessibility-information.full-ruby-annotations", compactString: fullRubyLocale.compact, descriptiveString: fullRubyLocale.descriptive }); } if (highAudioContrast) { const audioContrastLocale = Localization.getString("additional-accessibility-information.high-contrast-between-foreground-and-background-audio"); this.statements.push({ id: "additional-accessibility-information.high-contrast-between-foreground-and-background-audio", compactString: audioContrastLocale.compact, descriptiveString: audioContrastLocale.descriptive }); } if (highDisplayContrast) { const displayContrastLocale = Localization.getString("additional-accessibility-information.high-contrast-between-text-and-background"); this.statements.push({ id: "additional-accessibility-information.high-contrast-between-text-and-background", compactString: displayContrastLocale.compact, descriptiveString: displayContrastLocale.descriptive }); } if (largePrint) { const largePrintLocale = Localization.getString("additional-accessibility-information.large-print"); this.statements.push({ id: "additional-accessibility-information.large-print", compactString: largePrintLocale.compact, descriptiveString: largePrintLocale.descriptive }); } if (signLanguage) { const signLanguageLocale = Localization.getString("additional-accessibility-information.sign-language"); this.statements.push({ id: "additional-accessibility-information.sign-language", compactString: signLanguageLocale.compact, descriptiveString: signLanguageLocale.descriptive }); } if (tactileGraphics) { const tactileGraphicsLocale = Localization.getString("additional-accessibility-information.tactile-graphics"); this.statements.push({ id: "additional-accessibility-information.tactile-graphics", compactString: tactileGraphicsLocale.compact, descriptiveString: tactileGraphicsLocale.descriptive }); } if (tactileObjects) { const tactileObjectsLocale = Localization.getString("additional-accessibility-information.tactile-objects"); this.statements.push({ id: "additional-accessibility-information.tactile-objects", compactString: tactileObjectsLocale.compact, descriptiveString: tactileObjectsLocale.descriptive }); } if (textToSpeechHinting) { const ttsHintingLocale = Localization.getString("additional-accessibility-information.text-to-speech-hinting"); this.statements.push({ id: "additional-accessibility-information.text-to-speech-hinting", compactString: ttsHintingLocale.compact, descriptiveString: ttsHintingLocale.descriptive }); } } public static fromPublication(publication: Publication): AdditionalInformation { const a11y = publication.metadata.accessibility ?? new Accessibility(); const features = a11y.feature ?? []; return new AdditionalInformation( features.some(f => f.value === Feature.PAGE_BREAK_MARKERS.value || f.value === Feature.PRINT_PAGE_NUMBERS.value), features.some(f => f.value === Feature.ARIA.value), features.some(f => f.value === Feature.AUDIO_DESCRIPTION.value), features.some(f => f.value === Feature.BRAILLE.value), features.some(f => f.value === Feature.RUBY_ANNOTATIONS.value), features.some(f => f.value === Feature.FULL_RUBY_ANNOTATIONS.value), features.some(f => f.value === Feature.HIGH_CONTRAST_AUDIO.value), features.some(f => f.value === Feature.HIGH_CONTRAST_DISPLAY.value), features.some(f => f.value === Feature.LARGE_PRINT.value), features.some(f => f.value === Feature.SIGN_LANGUAGE.value), features.some(f => f.value === Feature.TACTILE_GRAPHIC.value), features.some(f => f.value === Feature.TACTILE_OBJECT.value), features.some(f => f.value === Feature.TTS_MARKUP.value) ); } } export enum HazardType { yes = 'yes', no = 'no', unknown = 'unknown', noMetadata = 'noMetadata' } /** * Represents potential hazards in the content */ export class Hazards implements AccessibilityDisplayField { public readonly id = "hazards.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly noMetadata: boolean; public readonly noHazards: boolean; public readonly unknown: boolean; public readonly flashing: HazardType; public readonly motion: HazardType; public readonly sound: HazardType; public readonly statements: AccessibilityDisplayStatement[]; private constructor( flashing: HazardType = HazardType.unknown, motion: HazardType = HazardType.unknown, sound: HazardType = HazardType.unknown ) { this.flashing = flashing; this.motion = motion; this.sound = sound; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.noMetadata = flashing === HazardType.noMetadata && motion === HazardType.noMetadata && sound === HazardType.noMetadata; this.noHazards = flashing === HazardType.no && motion === HazardType.no && sound === HazardType.no; this.unknown = flashing === HazardType.unknown && motion === HazardType.unknown && sound === HazardType.unknown; this.shouldDisplay = !this.noMetadata; this.statements = []; if (this.noHazards) { const noneLocale = Localization.getString("hazards.none"); this.statements.push({ id: "hazards.none", compactString: noneLocale.compact, descriptiveString: noneLocale.descriptive }); } else if (this.unknown) { const unknownLocale = Localization.getString("hazards.unknown"); this.statements.push({ id: "hazards.unknown", compactString: unknownLocale.compact, descriptiveString: unknownLocale.descriptive }); } else if (this.noMetadata) { const noMetadataLocale = Localization.getString("hazards.no-metadata"); this.statements.push({ id: "hazards.no-metadata", compactString: noMetadataLocale.compact, descriptiveString: noMetadataLocale.descriptive }); } else { if (flashing === HazardType.yes) { const flashingLocale = Localization.getString("hazards.flashing"); this.statements.push({ id: "hazards.flashing", compactString: flashingLocale.compact, descriptiveString: flashingLocale.descriptive }); } else if (flashing === HazardType.unknown) { const flashingUnknownLocale = Localization.getString("hazards.flashing-unknown"); this.statements.push({ id: "hazards.flashing-unknown", compactString: flashingUnknownLocale.compact, descriptiveString: flashingUnknownLocale.descriptive }); } else if (flashing === HazardType.no) { const flashingNoneLocale = Localization.getString("hazards.flashing-none"); this.statements.push({ id: "hazards.flashing-none", compactString: flashingNoneLocale.compact, descriptiveString: flashingNoneLocale.descriptive }); } if (motion === HazardType.yes) { const motionLocale = Localization.getString("hazards.motion"); this.statements.push({ id: "hazards.motion", compactString: motionLocale.compact, descriptiveString: motionLocale.descriptive }); } else if (motion === HazardType.unknown) { const motionUnknownLocale = Localization.getString("hazards.motion-unknown"); this.statements.push({ id: "hazards.motion-unknown", compactString: motionUnknownLocale.compact, descriptiveString: motionUnknownLocale.descriptive }); } else if (motion === HazardType.no) { const motionNoneLocale = Localization.getString("hazards.motion-none"); this.statements.push({ id: "hazards.motion-none", compactString: motionNoneLocale.compact, descriptiveString: motionNoneLocale.descriptive }); } if (sound === HazardType.yes) { const soundLocale = Localization.getString("hazards.sound"); this.statements.push({ id: "hazards.sound", compactString: soundLocale.compact, descriptiveString: soundLocale.descriptive }); } else if (sound === HazardType.unknown) { const soundUnknownLocale = Localization.getString("hazards.sound-unknown"); this.statements.push({ id: "hazards.sound-unknown", compactString: soundUnknownLocale.compact, descriptiveString: soundUnknownLocale.descriptive }); } else if (sound === HazardType.no) { const soundNoneLocale = Localization.getString("hazards.sound-none"); this.statements.push({ id: "hazards.sound-none", compactString: soundNoneLocale.compact, descriptiveString: soundNoneLocale.descriptive }); } } } public static fromPublication(publication: Publication): Hazards { const hazards = publication.metadata.accessibility?.hazard ?? []; let fallback: HazardType; if (hazards.some(h => h.value === Hazard.NONE.value)) { fallback = HazardType.no; } else if (hazards.some(h => h.value === Hazard.UNKNOWN.value)) { fallback = HazardType.unknown; } else { fallback = HazardType.noMetadata; } let flashing: HazardType; if (hazards.some(h => h.value === Hazard.FLASHING.value)) { flashing = HazardType.yes; } else if (hazards.some(h => h.value === Hazard.NO_FLASHING_HAZARD.value)) { flashing = HazardType.no; } else if (hazards.some(h => h.value === Hazard.UNKNOWN_FLASHING_HAZARD.value)) { flashing = HazardType.unknown; } else { flashing = fallback; } let motion: HazardType; if (hazards.some(h => h.value === Hazard.MOTION_SIMULATION.value)) { motion = HazardType.yes; } else if (hazards.some(h => h.value === Hazard.NO_MOTION_SIMULATION_HAZARD.value)) { motion = HazardType.no; } else if (hazards.some(h => h.value === Hazard.UNKNOWN_MOTION_SIMULATION_HAZARD.value)) { motion = HazardType.unknown; } else { motion = fallback; } let sound: HazardType; if (hazards.some(h => h.value === Hazard.SOUND.value)) { sound = HazardType.yes; } else if (hazards.some(h => h.value === Hazard.NO_SOUND_HAZARD.value)) { sound = HazardType.no; } else if (hazards.some(h => h.value === Hazard.UNKNOWN_SOUND_HAZARD.value)) { sound = HazardType.unknown; } else { sound = fallback; } return new Hazards(flashing, motion, sound); } } /** * Represents conformance to accessibility standards */ export class Conformance implements AccessibilityDisplayField { public readonly id = "conformance.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly profiles: AccessibilityProfile[]; public readonly statements: AccessibilityDisplayStatement[]; private constructor(profiles: AccessibilityProfile[] = []) { this.profiles = profiles; // This should be displayed even if there is no metadata this.shouldDisplay = true; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.statements = []; if (profiles.length === 0) { const noConformanceLocale = Localization.getString("conformance.no"); this.statements.push({ id: "conformance.no", compactString: noConformanceLocale.compact, descriptiveString: noConformanceLocale.descriptive }); return; } if (profiles.some(profile => profile.isWCAGLevelAAA)) { const aaaLocale = Localization.getString("conformance.aaa"); this.statements.push({ id: "conformance.aaa", compactString: aaaLocale.compact, descriptiveString: aaaLocale.descriptive }); } else if (profiles.some(profile => profile.isWCAGLevelAA)) { const aaLocale = Localization.getString("conformance.aa"); this.statements.push({ id: "conformance.aa", compactString: aaLocale.compact, descriptiveString: aaLocale.descriptive }); } else if (profiles.some(profile => profile.isWCAGLevelA)) { const aLocale = Localization.getString("conformance.a"); this.statements.push({ id: "conformance.a", compactString: aLocale.compact, descriptiveString: aLocale.descriptive }); } else { const unknownLocale = Localization.getString("conformance.unknown-standard"); this.statements.push({ id: "conformance.unknown-standard", compactString: unknownLocale.compact, descriptiveString: unknownLocale.descriptive }); } } public static fromPublication(publication: Publication): Conformance { const profiles = publication.metadata.accessibility?.conformsTo ?? []; return new Conformance(profiles); } } /** * Represents legal exemptions */ export class Legal implements AccessibilityDisplayField { public readonly id = "legal-considerations.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly exemption: boolean; public readonly statements: AccessibilityDisplayStatement[]; private constructor( exemption: boolean = false ) { this.exemption = exemption; this.shouldDisplay = this.exemption; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; this.statements = []; if (exemption) { const exemptLocale = Localization.getString("legal-considerations.exempt"); this.statements.push({ id: "legal-considerations.exempt", compactString: exemptLocale.compact, descriptiveString: exemptLocale.descriptive }); } else { const noMetadataLocale = Localization.getString("legal-considerations.no-metadata"); this.statements.push({ id: "legal-considerations.no-metadata", compactString: noMetadataLocale.compact, descriptiveString: noMetadataLocale.descriptive }); } } public static fromPublication(publication: Publication): Legal { const exemptions = publication.metadata.accessibility?.exemption ?? []; const hasExemption = exemptions.length > 0; return new Legal(hasExemption); } } /** * Represents the accessibility summary */ export class AccessibilitySummary implements AccessibilityDisplayField { public readonly id = "accessibility-summary.title"; public readonly title: string; public readonly shouldDisplay: boolean; public readonly statements: AccessibilityDisplayStatement[]; private constructor(publication: Publication) { this.shouldDisplay = true; const titleLocale = Localization.getString(this.id); this.title = titleLocale.compact; const summary = publication.metadata.accessibility?.summary; this.statements = []; if (this.shouldDisplay && summary) { this.statements.push({ id: "accessibility-summary.summary", compactString: summary, descriptiveString: summary }); } else { const locale = Localization.getString("accessibility-summary.no-metadata"); this.statements.push({ id: "accessibility-summary.no-metadata", compactString: locale.compact, descriptiveString: locale.descriptive }); } } public static fromPublication(publication: Publication): AccessibilitySummary { return new AccessibilitySummary(publication); } } /** * Main accessibility metadata display guide */ export class AccessibilityMetadataDisplayGuide { public readonly waysOfReading: WaysOfReading; public readonly navigation: Navigation; public readonly richContent: RichContent; public readonly additionalInformation: AdditionalInformation; public readonly hazards: Hazards; public readonly conformance: Conformance; public readonly legal: Legal; public readonly accessibilitySummary: AccessibilitySummary; public readonly fields: AccessibilityDisplayField[]; constructor(publication: Publication) { this.waysOfReading = WaysOfReading.fromPublication(publication); this.navigation = Navigation.fromPublication(publication); this.richContent = RichContent.fromPublication(publication); this.additionalInformation = AdditionalInformation.fromPublication(publication); this.hazards = Hazards.fromPublication(publication); this.conformance = Conformance.fromPublication(publication); this.legal = Legal.fromPublication(publication); this.accessibilitySummary = AccessibilitySummary.fromPublication(publication); this.fields = [ this.waysOfReading, this.navigation, this.richContent, this.additionalInformation, this.hazards, this.conformance, this.legal, this.accessibilitySummary ]; } }