add initial marp implementation with sample content and build configuration
This commit is contained in:
282
node_modules/mathjax-full/ts/a11y/explorer/Explorer.ts
generated
vendored
Normal file
282
node_modules/mathjax-full/ts/a11y/explorer/Explorer.ts
generated
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2009-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Explorers for A11Y purposes.
|
||||
*
|
||||
* @author v.sorge@mathjax.org (Volker Sorge)
|
||||
*/
|
||||
|
||||
|
||||
import {A11yDocument, Region} from './Region.js';
|
||||
import Sre from '../sre.js';
|
||||
|
||||
/**
|
||||
* A11y explorers.
|
||||
* @interface
|
||||
*/
|
||||
export interface Explorer {
|
||||
|
||||
/**
|
||||
* Flag indicating if the explorer is active.
|
||||
* @type {boolean}
|
||||
*/
|
||||
active: boolean;
|
||||
|
||||
/**
|
||||
* Flag indicating if event bubbling is stopped.
|
||||
* @type {boolean}
|
||||
*/
|
||||
stoppable: boolean;
|
||||
|
||||
/**
|
||||
* Attaches navigator and its event handlers to a node.
|
||||
*/
|
||||
Attach(): void;
|
||||
|
||||
/**
|
||||
* Detaches navigator and its event handlers to a node.
|
||||
*/
|
||||
Detach(): void;
|
||||
|
||||
/**
|
||||
* Starts the explorer.
|
||||
*/
|
||||
Start(): void;
|
||||
|
||||
/**
|
||||
* Stops the explorer.
|
||||
*/
|
||||
Stop(): void;
|
||||
|
||||
|
||||
/**
|
||||
* Adds the events of the explorer to the node's event listener.
|
||||
*/
|
||||
AddEvents(): void;
|
||||
|
||||
/**
|
||||
* Removes the events of the explorer from the node's event listener.
|
||||
*/
|
||||
RemoveEvents(): void;
|
||||
|
||||
/**
|
||||
* Update the explorer after state changes.
|
||||
* @param {boolean=} force Forces the update in any case. (E.g., even if
|
||||
* explorer is inactive.)
|
||||
*/
|
||||
Update(force?: boolean): void;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class implementing the very basic explorer functionality.
|
||||
*
|
||||
* Explorers use creator pattern to ensure they automatically attach themselves
|
||||
* to their node. This class provides the create method and is consequently not
|
||||
* declared abstract.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {Explorer}
|
||||
*
|
||||
* @template T The type that is consumed by the Region of this explorer.
|
||||
*/
|
||||
export class AbstractExplorer<T> implements Explorer {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public stoppable: boolean = true;
|
||||
|
||||
/**
|
||||
* Named events and their functions.
|
||||
* @type {[string, function(x: Event)][]}
|
||||
*/
|
||||
protected events: [string, (x: Event) => void][] = [];
|
||||
|
||||
/**
|
||||
* The Sre highlighter associated with the walker.
|
||||
* @type {Sre.highlighter}
|
||||
*/
|
||||
protected highlighter: Sre.highlighter = this.getHighlighter();
|
||||
|
||||
/**
|
||||
* Flag if explorer is active.
|
||||
* @type {boolean}
|
||||
*/
|
||||
private _active: boolean = false;
|
||||
|
||||
/**
|
||||
* Stops event bubbling.
|
||||
* @param {Event} event The event that is stopped.
|
||||
*/
|
||||
protected static stopEvent(event: Event) {
|
||||
if (event.preventDefault) {
|
||||
event.preventDefault();
|
||||
} else {
|
||||
event.returnValue = false;
|
||||
}
|
||||
if (event.stopImmediatePropagation) {
|
||||
event.stopImmediatePropagation();
|
||||
} else if (event.stopPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
event.cancelBubble = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creator pattern for explorers.
|
||||
* @param {A11yDocument} document The current document.
|
||||
* @param {Region<T>} region A region to display results.
|
||||
* @param {HTMLElement} node The node on which the explorer works.
|
||||
* @param {any[]} ...rest Remaining information.
|
||||
* @return {Explorer} An object of the particular explorer class.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
public static create<T>(
|
||||
document: A11yDocument,
|
||||
region: Region<T>,
|
||||
node: HTMLElement, ...rest: any[]
|
||||
): Explorer {
|
||||
let explorer = new this(document, region, node, ...rest);
|
||||
return explorer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {A11yDocument} document The current document.
|
||||
* @param {Region<T>} region A region to display results.
|
||||
* @param {HTMLElement} node The node on which the explorer works.
|
||||
* @param {any[]} ...rest Remaining information.
|
||||
*/
|
||||
protected constructor(
|
||||
public document: A11yDocument,
|
||||
protected region: Region<T>,
|
||||
protected node: HTMLElement, ..._rest: any[]
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {[string, (x: Event) => void][]} The events associated with this
|
||||
* explorer.
|
||||
*/
|
||||
protected Events(): [string, (x: Event) => void][] {
|
||||
return this.events;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public get active(): boolean {
|
||||
return this._active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public set active(flag: boolean) {
|
||||
this._active = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Attach() {
|
||||
this.AddEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Detach() {
|
||||
this.RemoveEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Start() {
|
||||
this.highlighter = this.getHighlighter();
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Stop() {
|
||||
if (this.active) {
|
||||
this.region.Clear();
|
||||
this.region.Hide();
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddEvents() {
|
||||
for (let [eventkind, eventfunc] of this.events) {
|
||||
this.node.addEventListener(eventkind, eventfunc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public RemoveEvents() {
|
||||
for (let [eventkind, eventfunc] of this.events) {
|
||||
this.node.removeEventListener(eventkind, eventfunc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
// @ts-ignore: unused variable
|
||||
public Update(force: boolean = false): void {}
|
||||
|
||||
|
||||
/**
|
||||
* @return {Sre.Highlighter} A highlighter for the explorer.
|
||||
*/
|
||||
protected getHighlighter(): Sre.highlighter {
|
||||
let opts = this.document.options.a11y;
|
||||
let foreground = {color: opts.foregroundColor.toLowerCase(),
|
||||
alpha: opts.foregroundOpacity / 100};
|
||||
let background = {color: opts.backgroundColor.toLowerCase(),
|
||||
alpha: opts.backgroundOpacity / 100};
|
||||
return Sre.getHighlighter(
|
||||
background, foreground,
|
||||
{renderer: this.document.outputJax.name, browser: 'v3'});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the events of this explorer from bubbling.
|
||||
* @param {Event} event The event to stop.
|
||||
*/
|
||||
protected stopEvent(event: Event) {
|
||||
if (this.stoppable) {
|
||||
AbstractExplorer.stopEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
460
node_modules/mathjax-full/ts/a11y/explorer/KeyExplorer.ts
generated
vendored
Normal file
460
node_modules/mathjax-full/ts/a11y/explorer/KeyExplorer.ts
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2009-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Explorers based on keyboard events.
|
||||
*
|
||||
* @author v.sorge@mathjax.org (Volker Sorge)
|
||||
*/
|
||||
|
||||
|
||||
import {A11yDocument, Region} from './Region.js';
|
||||
import {Explorer, AbstractExplorer} from './Explorer.js';
|
||||
import Sre from '../sre.js';
|
||||
|
||||
|
||||
/**
|
||||
* Interface for keyboard explorers. Adds the necessary keyboard events.
|
||||
* @interface
|
||||
* @extends {Explorer}
|
||||
*/
|
||||
export interface KeyExplorer extends Explorer {
|
||||
|
||||
/**
|
||||
* Function to be executed on key down.
|
||||
* @param {KeyboardEvent} event The keyboard event.
|
||||
*/
|
||||
KeyDown(event: KeyboardEvent): void;
|
||||
|
||||
/**
|
||||
* Function to be executed on focus in.
|
||||
* @param {KeyboardEvent} event The keyboard event.
|
||||
*/
|
||||
FocusIn(event: FocusEvent): void;
|
||||
|
||||
/**
|
||||
* Function to be executed on focus out.
|
||||
* @param {KeyboardEvent} event The keyboard event.
|
||||
*/
|
||||
FocusOut(event: FocusEvent): void;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AbstractExplorer}
|
||||
*
|
||||
* @template T The type that is consumed by the Region of this explorer.
|
||||
*/
|
||||
export abstract class AbstractKeyExplorer<T> extends AbstractExplorer<T> implements KeyExplorer {
|
||||
|
||||
/**
|
||||
* Flag indicating if the explorer is attached to an object.
|
||||
*/
|
||||
public attached: boolean = false;
|
||||
|
||||
/**
|
||||
* The attached Sre walker.
|
||||
* @type {Walker}
|
||||
*/
|
||||
protected walker: Sre.walker;
|
||||
|
||||
private eventsAttached: boolean = false;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected events: [string, (x: Event) => void][] =
|
||||
super.Events().concat(
|
||||
[['keydown', this.KeyDown.bind(this)],
|
||||
['focusin', this.FocusIn.bind(this)],
|
||||
['focusout', this.FocusOut.bind(this)]]);
|
||||
|
||||
/**
|
||||
* The original tabindex value before explorer was attached.
|
||||
* @type {boolean}
|
||||
*/
|
||||
private oldIndex: number = null;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public abstract KeyDown(event: KeyboardEvent): void;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public FocusIn(_event: FocusEvent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public FocusOut(_event: FocusEvent) {
|
||||
this.Stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update(force: boolean = false) {
|
||||
if (!this.active && !force) return;
|
||||
this.highlighter.unhighlight();
|
||||
let nodes = this.walker.getFocus(true).getNodes();
|
||||
if (!nodes.length) {
|
||||
this.walker.refocus();
|
||||
nodes = this.walker.getFocus().getNodes();
|
||||
}
|
||||
this.highlighter.highlight(nodes as HTMLElement[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Attach() {
|
||||
super.Attach();
|
||||
this.attached = true;
|
||||
this.oldIndex = this.node.tabIndex;
|
||||
this.node.tabIndex = 1;
|
||||
this.node.setAttribute('role', 'application');
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddEvents() {
|
||||
if (!this.eventsAttached) {
|
||||
super.AddEvents();
|
||||
this.eventsAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Detach() {
|
||||
if (this.active) {
|
||||
this.node.tabIndex = this.oldIndex;
|
||||
this.oldIndex = null;
|
||||
this.node.removeAttribute('role');
|
||||
}
|
||||
this.attached = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Stop() {
|
||||
if (this.active) {
|
||||
this.highlighter.unhighlight();
|
||||
this.walker.deactivate();
|
||||
}
|
||||
super.Stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Explorer that pushes speech to live region.
|
||||
* @constructor
|
||||
* @extends {AbstractKeyExplorer}
|
||||
*/
|
||||
export class SpeechExplorer extends AbstractKeyExplorer<string> {
|
||||
|
||||
private static updatePromise = Promise.resolve();
|
||||
|
||||
/**
|
||||
* The Sre speech generator associated with the walker.
|
||||
* @type {SpeechGenerator}
|
||||
*/
|
||||
public speechGenerator: Sre.speechGenerator;
|
||||
|
||||
/**
|
||||
* The name of the option used to control when this is being shown
|
||||
* @type {string}
|
||||
*/
|
||||
public showRegion: string = 'subtitles';
|
||||
|
||||
private init: boolean = false;
|
||||
|
||||
/**
|
||||
* Flag in case the start method is triggered before the walker is fully
|
||||
* initialised. I.e., we have to wait for Sre. Then region is re-shown if
|
||||
* necessary, as otherwise it leads to incorrect stacking.
|
||||
* @type {boolean}
|
||||
*/
|
||||
private restarted: boolean = false;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AbstractKeyExplorer}
|
||||
*/
|
||||
constructor(public document: A11yDocument,
|
||||
protected region: Region<string>,
|
||||
protected node: HTMLElement,
|
||||
private mml: string) {
|
||||
super(document, region, node);
|
||||
this.initWalker();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Start() {
|
||||
if (!this.attached) return;
|
||||
let options = this.getOptions();
|
||||
if (!this.init) {
|
||||
this.init = true;
|
||||
SpeechExplorer.updatePromise = SpeechExplorer.updatePromise.then(async () => {
|
||||
return Sre.sreReady()
|
||||
.then(() => Sre.setupEngine({locale: options.locale}))
|
||||
.then(() => {
|
||||
// Important that both are in the same block so speech explorers
|
||||
// are restarted sequentially.
|
||||
this.Speech(this.walker);
|
||||
this.Start();
|
||||
});
|
||||
})
|
||||
.catch((error: Error) => console.log(error.message));
|
||||
return;
|
||||
}
|
||||
super.Start();
|
||||
this.speechGenerator = Sre.getSpeechGenerator('Direct');
|
||||
this.speechGenerator.setOptions(options);
|
||||
this.walker = Sre.getWalker(
|
||||
'table', this.node, this.speechGenerator, this.highlighter, this.mml);
|
||||
this.walker.activate();
|
||||
this.Update();
|
||||
if (this.document.options.a11y[this.showRegion]) {
|
||||
SpeechExplorer.updatePromise.then(
|
||||
() => this.region.Show(this.node, this.highlighter));
|
||||
}
|
||||
this.restarted = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update(force: boolean = false) {
|
||||
super.Update(force);
|
||||
let options = this.speechGenerator.getOptions();
|
||||
// This is a necessary in case speech options have changed via keypress
|
||||
// during walking.
|
||||
if (options.modality === 'speech') {
|
||||
this.document.options.sre.domain = options.domain;
|
||||
this.document.options.sre.style = options.style;
|
||||
this.document.options.a11y.speechRules =
|
||||
options.domain + '-' + options.style;
|
||||
}
|
||||
SpeechExplorer.updatePromise = SpeechExplorer.updatePromise.then(async () => {
|
||||
return Sre.sreReady()
|
||||
.then(() => Sre.setupEngine({modality: options.modality,
|
||||
locale: options.locale}))
|
||||
.then(() => this.region.Update(this.walker.speech()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the speech for the current expression once Sre is ready.
|
||||
* @param {Walker} walker The sre walker.
|
||||
*/
|
||||
public Speech(walker: Sre.walker) {
|
||||
SpeechExplorer.updatePromise.then(() => {
|
||||
walker.speech();
|
||||
this.node.setAttribute('hasspeech', 'true');
|
||||
this.Update();
|
||||
if (this.restarted && this.document.options.a11y[this.showRegion]) {
|
||||
this.region.Show(this.node, this.highlighter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public KeyDown(event: KeyboardEvent) {
|
||||
const code = event.keyCode;
|
||||
this.walker.modifier = event.shiftKey;
|
||||
if (code === 27) {
|
||||
this.Stop();
|
||||
this.stopEvent(event);
|
||||
return;
|
||||
}
|
||||
if (this.active) {
|
||||
this.Move(code);
|
||||
if (this.triggerLink(code)) return;
|
||||
this.stopEvent(event);
|
||||
return;
|
||||
}
|
||||
if (code === 32 && event.shiftKey || code === 13) {
|
||||
this.Start();
|
||||
this.stopEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically triggers a link if the focused node contains one.
|
||||
* @param {number} code The keycode of the last key pressed.
|
||||
*/
|
||||
protected triggerLink(code: number) {
|
||||
if (code !== 13) {
|
||||
return false;
|
||||
}
|
||||
let node = this.walker.getFocus().getNodes()?.[0];
|
||||
let focus = node?.
|
||||
getAttribute('data-semantic-postfix')?.
|
||||
match(/(^| )link($| )/);
|
||||
if (focus) {
|
||||
node.parentNode.dispatchEvent(new MouseEvent('click'));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Move(key: number) {
|
||||
this.walker.move(key);
|
||||
this.Update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the Sre walker.
|
||||
*/
|
||||
private initWalker() {
|
||||
this.speechGenerator = Sre.getSpeechGenerator('Tree');
|
||||
let dummy = Sre.getWalker(
|
||||
'dummy', this.node, this.speechGenerator, this.highlighter, this.mml);
|
||||
this.walker = dummy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the speech options to sync with document options.
|
||||
* @return {{[key: string]: string}} The options settings for the speech
|
||||
* generator.
|
||||
*/
|
||||
private getOptions(): {[key: string]: string} {
|
||||
let options = this.speechGenerator.getOptions();
|
||||
let sreOptions = this.document.options.sre;
|
||||
if (options.modality === 'speech' &&
|
||||
(options.locale !== sreOptions.locale ||
|
||||
options.domain !== sreOptions.domain ||
|
||||
options.style !== sreOptions.style)) {
|
||||
options.domain = sreOptions.domain;
|
||||
options.style = sreOptions.style;
|
||||
options.locale = sreOptions.locale;
|
||||
this.walker.update(options);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Explorer that magnifies what is currently explored. Uses a hover region.
|
||||
* @constructor
|
||||
* @extends {AbstractKeyExplorer}
|
||||
*/
|
||||
export class Magnifier extends AbstractKeyExplorer<HTMLElement> {
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AbstractKeyExplorer}
|
||||
*/
|
||||
constructor(public document: A11yDocument,
|
||||
protected region: Region<HTMLElement>,
|
||||
protected node: HTMLElement,
|
||||
private mml: string) {
|
||||
super(document, region, node);
|
||||
this.walker = Sre.getWalker(
|
||||
'table', this.node, Sre.getSpeechGenerator('Dummy'),
|
||||
this.highlighter, this.mml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update(force: boolean = false) {
|
||||
super.Update(force);
|
||||
this.showFocus();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Start() {
|
||||
super.Start();
|
||||
if (!this.attached) return;
|
||||
this.region.Show(this.node, this.highlighter);
|
||||
this.walker.activate();
|
||||
this.Update();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows the nodes that are currently focused.
|
||||
*/
|
||||
private showFocus() {
|
||||
let node = this.walker.getFocus().getNodes()[0] as HTMLElement;
|
||||
this.region.Show(node, this.highlighter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Move(key: number) {
|
||||
let result = this.walker.move(key);
|
||||
if (result) {
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public KeyDown(event: KeyboardEvent) {
|
||||
const code = event.keyCode;
|
||||
this.walker.modifier = event.shiftKey;
|
||||
if (code === 27) {
|
||||
this.Stop();
|
||||
this.stopEvent(event);
|
||||
return;
|
||||
}
|
||||
if (this.active && code !== 13) {
|
||||
this.Move(code);
|
||||
this.stopEvent(event);
|
||||
return;
|
||||
}
|
||||
if (code === 32 && event.shiftKey || code === 13) {
|
||||
this.Start();
|
||||
this.stopEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
224
node_modules/mathjax-full/ts/a11y/explorer/MouseExplorer.ts
generated
vendored
Normal file
224
node_modules/mathjax-full/ts/a11y/explorer/MouseExplorer.ts
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2009-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Explorers based on mouse events.
|
||||
*
|
||||
* @author v.sorge@mathjax.org (Volker Sorge)
|
||||
*/
|
||||
|
||||
|
||||
import {A11yDocument, DummyRegion, Region} from './Region.js';
|
||||
import {Explorer, AbstractExplorer} from './Explorer.js';
|
||||
import '../sre.js';
|
||||
|
||||
|
||||
/**
|
||||
* Interface for mouse explorers. Adds the necessary mouse events.
|
||||
* @interface
|
||||
* @extends {Explorer}
|
||||
*/
|
||||
export interface MouseExplorer extends Explorer {
|
||||
|
||||
/**
|
||||
* Function to be executed on mouse over.
|
||||
* @param {MouseEvent} event The mouse event.
|
||||
*/
|
||||
MouseOver(event: MouseEvent): void;
|
||||
|
||||
/**
|
||||
* Function to be executed on mouse out.
|
||||
* @param {MouseEvent} event The mouse event.
|
||||
*/
|
||||
MouseOut(event: MouseEvent): void;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AbstractExplorer}
|
||||
*
|
||||
* @template T The type that is consumed by the Region of this explorer.
|
||||
*/
|
||||
export abstract class AbstractMouseExplorer<T> extends AbstractExplorer<T> implements MouseExplorer {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected events: [string, (x: Event) => void][] =
|
||||
super.Events().concat([
|
||||
['mouseover', this.MouseOver.bind(this)],
|
||||
['mouseout', this.MouseOut.bind(this)]
|
||||
]);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public MouseOver(_event: MouseEvent) {
|
||||
this.Start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public MouseOut(_event: MouseEvent) {
|
||||
this.Stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exploration via hovering.
|
||||
* @constructor
|
||||
* @extends {AbstractMouseExplorer}
|
||||
*/
|
||||
export abstract class Hoverer<T> extends AbstractMouseExplorer<T> {
|
||||
|
||||
/**
|
||||
* Remember the last position to avoid flickering.
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
protected coord: [number, number];
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {AbstractMouseExplorer<T>}
|
||||
*
|
||||
* @param {A11yDocument} document The current document.
|
||||
* @param {Region<T>} region A region to display results.
|
||||
* @param {HTMLElement} node The node on which the explorer works.
|
||||
* @param {(node: HTMLElement) => boolean} nodeQuery Predicate on nodes that
|
||||
* will fire the hoverer.
|
||||
* @param {(node: HTMLElement) => T} nodeAccess Accessor to extract node value
|
||||
* that is passed to the region.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
protected constructor(public document: A11yDocument,
|
||||
protected region: Region<T>,
|
||||
protected node: HTMLElement,
|
||||
protected nodeQuery: (node: HTMLElement) => boolean,
|
||||
protected nodeAccess: (node: HTMLElement) => T) {
|
||||
super(document, region, node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public MouseOut(event: MouseEvent) {
|
||||
if (event.clientX === this.coord[0] &&
|
||||
event.clientY === this.coord[1]) {
|
||||
return;
|
||||
}
|
||||
this.highlighter.unhighlight();
|
||||
this.region.Hide();
|
||||
super.MouseOut(event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public MouseOver(event: MouseEvent) {
|
||||
super.MouseOver(event);
|
||||
let target = event.target as HTMLElement;
|
||||
this.coord = [event.clientX, event.clientY];
|
||||
let [node, kind] = this.getNode(target);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
this.highlighter.unhighlight();
|
||||
this.highlighter.highlight([node]);
|
||||
this.region.Update(kind);
|
||||
this.region.Show(node, this.highlighter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the closest node on which the node query fires. Thereby closest
|
||||
* is defined as:
|
||||
* 1. The node or its ancestor on which the query is true.
|
||||
* 2. In case 1 does not exist the left-most child on which query is true.
|
||||
* 3. Otherwise fails.
|
||||
*
|
||||
* @param {HTMLElement} node The node on which the mouse event fired.
|
||||
* @return {[HTMLElement, T]} Node and output pair if successful.
|
||||
*/
|
||||
public getNode(node: HTMLElement): [HTMLElement, T] {
|
||||
let original = node;
|
||||
while (node && node !== this.node) {
|
||||
if (this.nodeQuery(node)) {
|
||||
return [node, this.nodeAccess(node)];
|
||||
}
|
||||
node = node.parentNode as HTMLElement;
|
||||
}
|
||||
node = original;
|
||||
while (node) {
|
||||
if (this.nodeQuery(node)) {
|
||||
return [node, this.nodeAccess(node)];
|
||||
}
|
||||
let child = node.childNodes[0] as HTMLElement;
|
||||
node = (child && child.tagName === 'defs') ? // This is for SVG.
|
||||
node.childNodes[1] as HTMLElement : child;
|
||||
}
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hoverer that displays information on nodes (e.g., as tooltips).
|
||||
* @constructor
|
||||
* @extends {Hoverer}
|
||||
*/
|
||||
export class ValueHoverer extends Hoverer<string> { }
|
||||
|
||||
|
||||
/**
|
||||
* Hoverer that displays node content (e.g., for magnification).
|
||||
* @constructor
|
||||
* @extends {Hoverer}
|
||||
*/
|
||||
export class ContentHoverer extends Hoverer<HTMLElement> { }
|
||||
|
||||
|
||||
/**
|
||||
* Highlights maction nodes on hovering.
|
||||
* @constructor
|
||||
* @extends {Hoverer}
|
||||
*/
|
||||
export class FlameHoverer extends Hoverer<void> {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected constructor(
|
||||
public document: A11yDocument,
|
||||
_ignore: any,
|
||||
protected node: HTMLElement) {
|
||||
super(document, new DummyRegion(document), node,
|
||||
x => this.highlighter.isMactionNode(x),
|
||||
() => {});
|
||||
}
|
||||
|
||||
}
|
||||
519
node_modules/mathjax-full/ts/a11y/explorer/Region.ts
generated
vendored
Normal file
519
node_modules/mathjax-full/ts/a11y/explorer/Region.ts
generated
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2009-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Regions for A11y purposes.
|
||||
*
|
||||
* @author v.sorge@mathjax.org (Volker Sorge)
|
||||
*/
|
||||
|
||||
|
||||
import {MathDocument} from '../../core/MathDocument.js';
|
||||
import {CssStyles} from '../../util/StyleList.js';
|
||||
import Sre from '../sre.js';
|
||||
|
||||
export type A11yDocument = MathDocument<HTMLElement, Text, Document>;
|
||||
|
||||
export interface Region<T> {
|
||||
|
||||
/**
|
||||
* Adds a style sheet for the live region to the document.
|
||||
*/
|
||||
AddStyles(): void;
|
||||
|
||||
/**
|
||||
* Adds the region element to the document.
|
||||
*/
|
||||
AddElement(): void;
|
||||
|
||||
/**
|
||||
* Shows the live region in the document.
|
||||
* @param {HTMLElement} node
|
||||
* @param {Sre.highlighter} highlighter
|
||||
*/
|
||||
Show(node: HTMLElement, highlighter: Sre.highlighter): void;
|
||||
|
||||
/**
|
||||
* Takes the element out of the document flow.
|
||||
*/
|
||||
Hide(): void;
|
||||
|
||||
/**
|
||||
* Clears the content of the region.
|
||||
*/
|
||||
Clear(): void;
|
||||
|
||||
/**
|
||||
* Updates the content of the region.
|
||||
* @template T
|
||||
*/
|
||||
Update(content: T): void;
|
||||
|
||||
}
|
||||
|
||||
export abstract class AbstractRegion<T> implements Region<T> {
|
||||
|
||||
/**
|
||||
* CSS Classname of the element.
|
||||
* @type {String}
|
||||
*/
|
||||
protected static className: string;
|
||||
|
||||
/**
|
||||
* True if the style has already been added to the document.
|
||||
* @type {boolean}
|
||||
*/
|
||||
protected static styleAdded: boolean = false;
|
||||
|
||||
/**
|
||||
* The CSS style that needs to be added for this type of region.
|
||||
* @type {CssStyles}
|
||||
*/
|
||||
protected static style: CssStyles;
|
||||
|
||||
/**
|
||||
* The outer div node.
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
protected div: HTMLElement;
|
||||
|
||||
/**
|
||||
* The inner node.
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
protected inner: HTMLElement;
|
||||
|
||||
/**
|
||||
* The actual class name to refer to static elements of a class.
|
||||
* @type {typeof AbstractRegion}
|
||||
*/
|
||||
protected CLASS: typeof AbstractRegion;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {A11yDocument} document The document the live region is added to.
|
||||
*/
|
||||
constructor(public document: A11yDocument) {
|
||||
this.CLASS = this.constructor as typeof AbstractRegion;
|
||||
this.AddStyles();
|
||||
this.AddElement();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddStyles() {
|
||||
if (this.CLASS.styleAdded) {
|
||||
return;
|
||||
}
|
||||
// TODO: should that be added to document.documentStyleSheet()?
|
||||
let node = this.document.adaptor.node('style');
|
||||
node.innerHTML = this.CLASS.style.cssText;
|
||||
this.document.adaptor.head(this.document.adaptor.document).
|
||||
appendChild(node);
|
||||
this.CLASS.styleAdded = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddElement() {
|
||||
let element = this.document.adaptor.node('div');
|
||||
element.classList.add(this.CLASS.className);
|
||||
element.style.backgroundColor = 'white';
|
||||
this.div = element;
|
||||
this.inner = this.document.adaptor.node('div');
|
||||
this.div.appendChild(this.inner);
|
||||
this.document.adaptor.
|
||||
body(this.document.adaptor.document).
|
||||
appendChild(this.div);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Show(node: HTMLElement, highlighter: Sre.highlighter) {
|
||||
this.position(node);
|
||||
this.highlight(highlighter);
|
||||
this.div.classList.add(this.CLASS.className + '_Show');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the position where to place the element wrt. to the given node.
|
||||
* @param {HTMLElement} node The reference node.
|
||||
*/
|
||||
protected abstract position(node: HTMLElement): void;
|
||||
|
||||
|
||||
/**
|
||||
* Highlights the region.
|
||||
* @param {Sre.highlighter} highlighter The Sre highlighter.
|
||||
*/
|
||||
protected abstract highlight(highlighter: Sre.highlighter): void;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Hide() {
|
||||
this.div.classList.remove(this.CLASS.className + '_Show');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public abstract Clear(): void;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public abstract Update(content: T): void;
|
||||
|
||||
|
||||
/**
|
||||
* Auxiliary position method that stacks shown regions of the same type.
|
||||
* @param {HTMLElement} node The reference node.
|
||||
*/
|
||||
protected stackRegions(node: HTMLElement) {
|
||||
// TODO: This could be made more efficient by caching regions of a class.
|
||||
const rect = node.getBoundingClientRect();
|
||||
let baseBottom = 0;
|
||||
let baseLeft = Number.POSITIVE_INFINITY;
|
||||
let regions = this.document.adaptor.document.getElementsByClassName(
|
||||
this.CLASS.className + '_Show');
|
||||
// Get all the shown regions (one is this element!) and append at bottom.
|
||||
for (let i = 0, region; region = regions[i]; i++) {
|
||||
if (region !== this.div) {
|
||||
baseBottom = Math.max(region.getBoundingClientRect().bottom, baseBottom);
|
||||
baseLeft = Math.min(region.getBoundingClientRect().left, baseLeft);
|
||||
}
|
||||
}
|
||||
const bot = (baseBottom ? baseBottom : rect.bottom + 10) + window.pageYOffset;
|
||||
const left = (baseLeft < Number.POSITIVE_INFINITY ? baseLeft : rect.left) + window.pageXOffset;
|
||||
this.div.style.top = bot + 'px';
|
||||
this.div.style.left = left + 'px';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DummyRegion extends AbstractRegion<void> {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Clear() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Hide() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Show() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddElement() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public AddStyles() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public position() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public highlight(_highlighter: Sre.highlighter) {}
|
||||
}
|
||||
|
||||
|
||||
export class StringRegion extends AbstractRegion<string> {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Clear(): void {
|
||||
this.Update('');
|
||||
this.inner.style.top = '';
|
||||
this.inner.style.backgroundColor = '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update(speech: string) {
|
||||
this.inner.textContent = '';
|
||||
this.inner.textContent = speech;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected position(node: HTMLElement) {
|
||||
this.stackRegions(node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected highlight(highlighter: Sre.highlighter) {
|
||||
const color = highlighter.colorString();
|
||||
this.inner.style.backgroundColor = color.background;
|
||||
this.inner.style.color = color.foreground;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class ToolTip extends StringRegion {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static className = 'MJX_ToolTip';
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static style: CssStyles =
|
||||
new CssStyles({
|
||||
['.' + ToolTip.className]: {
|
||||
position: 'absolute', display: 'inline-block',
|
||||
height: '1px', width: '1px'
|
||||
},
|
||||
['.' + ToolTip.className + '_Show']: {
|
||||
width: 'auto', height: 'auto', opacity: 1, 'text-align': 'center',
|
||||
'border-radius': '6px', padding: '0px 0px',
|
||||
'border-bottom': '1px dotted black', position: 'absolute',
|
||||
'z-index': 202
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class LiveRegion extends StringRegion {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static className = 'MJX_LiveRegion';
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static style: CssStyles =
|
||||
new CssStyles({
|
||||
['.' + LiveRegion.className]: {
|
||||
position: 'absolute', top: '0', height: '1px', width: '1px',
|
||||
padding: '1px', overflow: 'hidden'
|
||||
},
|
||||
['.' + LiveRegion.className + '_Show']: {
|
||||
top: '0', position: 'absolute', width: 'auto', height: 'auto',
|
||||
padding: '0px 0px', opacity: 1, 'z-index': '202',
|
||||
left: 0, right: 0, 'margin': '0 auto',
|
||||
'background-color': 'rgba(0, 0, 255, 0.2)', 'box-shadow': '0px 10px 20px #888',
|
||||
border: '2px solid #CCCCCC'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {A11yDocument} document The document the live region is added to.
|
||||
*/
|
||||
constructor(public document: A11yDocument) {
|
||||
super(document);
|
||||
this.div.setAttribute('aria-live', 'assertive');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Region that overlays the current element.
|
||||
export class HoverRegion extends AbstractRegion<HTMLElement> {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static className = 'MJX_HoverRegion';
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected static style: CssStyles =
|
||||
new CssStyles({
|
||||
['.' + HoverRegion.className]: {
|
||||
position: 'absolute', height: '1px', width: '1px',
|
||||
padding: '1px', overflow: 'hidden'
|
||||
},
|
||||
['.' + HoverRegion.className + '_Show']: {
|
||||
position: 'absolute', width: 'max-content', height: 'auto',
|
||||
padding: '0px 0px', opacity: 1, 'z-index': '202', 'margin': '0 auto',
|
||||
'background-color': 'rgba(0, 0, 255, 0.2)',
|
||||
'box-shadow': '0px 10px 20px #888', border: '2px solid #CCCCCC'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {A11yDocument} document The document the live region is added to.
|
||||
*/
|
||||
constructor(public document: A11yDocument) {
|
||||
super(document);
|
||||
this.inner.style.lineHeight = '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the region with respect to align parameter. There are
|
||||
* three options: top, bottom and center. Center is the default.
|
||||
*
|
||||
* @param {HTMLElement} node The node that is displayed.
|
||||
*/
|
||||
protected position(node: HTMLElement) {
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
const divRect = this.div.getBoundingClientRect();
|
||||
const xCenter = nodeRect.left + (nodeRect.width / 2);
|
||||
let left = xCenter - (divRect.width / 2);
|
||||
left = (left < 0) ? 0 : left;
|
||||
left = left + window.pageXOffset;
|
||||
let top;
|
||||
switch (this.document.options.a11y.align) {
|
||||
case 'top':
|
||||
top = nodeRect.top - divRect.height - 10 ;
|
||||
break;
|
||||
case 'bottom':
|
||||
top = nodeRect.bottom + 10;
|
||||
break;
|
||||
case 'center':
|
||||
default:
|
||||
const yCenter = nodeRect.top + (nodeRect.height / 2);
|
||||
top = yCenter - (divRect.height / 2);
|
||||
}
|
||||
top = top + window.pageYOffset;
|
||||
top = (top < 0) ? 0 : top;
|
||||
this.div.style.top = top + 'px';
|
||||
this.div.style.left = left + 'px';
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected highlight(highlighter: Sre.highlighter) {
|
||||
// TODO Do this with styles to avoid the interaction of SVG/CHTML.
|
||||
if (this.inner.firstChild &&
|
||||
!(this.inner.firstChild as HTMLElement).hasAttribute('sre-highlight')) {
|
||||
return;
|
||||
}
|
||||
const color = highlighter.colorString();
|
||||
this.inner.style.backgroundColor = color.background;
|
||||
this.inner.style.color = color.foreground;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Show(node: HTMLElement, highlighter: Sre.highlighter) {
|
||||
this.div.style.fontSize = this.document.options.a11y.magnify;
|
||||
this.Update(node);
|
||||
super.Show(node, highlighter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Clear() {
|
||||
this.inner.textContent = '';
|
||||
this.inner.style.top = '';
|
||||
this.inner.style.backgroundColor = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Update(node: HTMLElement) {
|
||||
this.Clear();
|
||||
let mjx = this.cloneNode(node);
|
||||
this.inner.appendChild(mjx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the node to put into the hover region.
|
||||
* @param {HTMLElement} node The original node.
|
||||
* @return {HTMLElement} The cloned node.
|
||||
*/
|
||||
private cloneNode(node: HTMLElement): HTMLElement {
|
||||
let mjx = node.cloneNode(true) as HTMLElement;
|
||||
if (mjx.nodeName !== 'MJX-CONTAINER') {
|
||||
// remove element spacing (could be done in CSS)
|
||||
if (mjx.nodeName !== 'g') {
|
||||
mjx.style.marginLeft = mjx.style.marginRight = '0';
|
||||
}
|
||||
let container = node;
|
||||
while (container && container.nodeName !== 'MJX-CONTAINER') {
|
||||
container = container.parentNode as HTMLElement;
|
||||
}
|
||||
if (mjx.nodeName !== 'MJX-MATH' && mjx.nodeName !== 'svg') {
|
||||
const child = container.firstChild;
|
||||
mjx = child.cloneNode(false).appendChild(mjx).parentNode as HTMLElement;
|
||||
//
|
||||
// SVG specific
|
||||
//
|
||||
if (mjx.nodeName === 'svg') {
|
||||
(mjx.firstChild as HTMLElement).setAttribute('transform', 'matrix(1 0 0 -1 0 0)');
|
||||
const W = parseFloat(mjx.getAttribute('viewBox').split(/ /)[2]);
|
||||
const w = parseFloat(mjx.getAttribute('width'));
|
||||
const {x, y, width, height} = (node as any).getBBox();
|
||||
mjx.setAttribute('viewBox', [x, -(y + height), width, height].join(' '));
|
||||
mjx.removeAttribute('style');
|
||||
mjx.setAttribute('width', (w / W * width) + 'ex');
|
||||
mjx.setAttribute('height', (w / W * height) + 'ex');
|
||||
container.setAttribute('sre-highlight', 'false');
|
||||
}
|
||||
}
|
||||
mjx = container.cloneNode(false).appendChild(mjx).parentNode as HTMLElement;
|
||||
// remove displayed math margins (could be done in CSS)
|
||||
mjx.style.margin = '0';
|
||||
}
|
||||
return mjx;
|
||||
}
|
||||
|
||||
}
|
||||
124
node_modules/mathjax-full/ts/a11y/explorer/TreeExplorer.ts
generated
vendored
Normal file
124
node_modules/mathjax-full/ts/a11y/explorer/TreeExplorer.ts
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*************************************************************
|
||||
*
|
||||
* Copyright (c) 2009-2022 The MathJax Consortium
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Tree Explorers allow to switch on effects on the entire
|
||||
* expression tree.
|
||||
*
|
||||
* @author v.sorge@mathjax.org (Volker Sorge)
|
||||
*/
|
||||
|
||||
|
||||
import {A11yDocument, Region} from './Region.js';
|
||||
import {Explorer, AbstractExplorer} from './Explorer.js';
|
||||
import Sre from '../sre.js';
|
||||
|
||||
export interface TreeExplorer extends Explorer {
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class AbstractTreeExplorer extends AbstractExplorer<void> {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected constructor(public document: A11yDocument,
|
||||
protected region: Region<void>,
|
||||
protected node: HTMLElement,
|
||||
protected mml: HTMLElement) {
|
||||
super(document, null, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public readonly stoppable = false;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Attach() {
|
||||
super.Attach();
|
||||
this.Start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Detach() {
|
||||
this.Stop();
|
||||
super.Detach();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class FlameColorer extends AbstractTreeExplorer {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Start() {
|
||||
if (this.active) return;
|
||||
this.active = true;
|
||||
this.highlighter.highlightAll(this.node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Stop() {
|
||||
if (this.active) {
|
||||
this.highlighter.unhighlightAll();
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class TreeColorer extends AbstractTreeExplorer {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Start() {
|
||||
if (this.active) return;
|
||||
this.active = true;
|
||||
let generator = Sre.getSpeechGenerator('Color');
|
||||
if (!this.node.hasAttribute('hasforegroundcolor')) {
|
||||
generator.generateSpeech(this.node, this.mml);
|
||||
this.node.setAttribute('hasforegroundcolor', 'true');
|
||||
}
|
||||
// TODO: Make this cleaner in Sre.
|
||||
(this.highlighter as any).colorizeAll(this.node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public Stop() {
|
||||
if (this.active) {
|
||||
(this.highlighter as any).uncolorizeAll(this.node);
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user