Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ts/a11y/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export function ExplorerMathDocumentMixin<
viewBraille: false, // display Braille output as subtitles
voicing: false, // switch on speech output
help: true, // include "press h for help" messages on focus
roleDescription: 'math', // the role description to use for math expressions
}
};

Expand Down Expand Up @@ -429,6 +430,7 @@ export function ExplorerMathDocumentMixin<
options.a11y.speechRules = `${options.sre.domain}-${options.sre.style}`;
}
options.MathItem = ExplorerMathItemMixin(options.MathItem, toMathML);
options.MathItem.roleDescription = options.roleDescription;
this.explorerRegions = new RegionPool(this);
if ('addStyles' in this) {
(this as any).addStyles(
Expand Down
10 changes: 7 additions & 3 deletions ts/a11y/explorer/KeyExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,11 @@ export class SpeechExplorer
*/
public FocusIn(_event: FocusEvent) {
if (this.item.outputData.nofocus) {
return; // we are refocusing after the menu has closed
//
// we are refocusing after a menu or dialog box has closed
//
this.item.outputData.nofocus = false;
return;
}
if (!this.clicked) {
this.Start();
Expand Down Expand Up @@ -421,7 +425,7 @@ export class SpeechExplorer
* @param {MouseEvent} event The mouse down event
*/
private MouseDown(event: MouseEvent) {
if (hasModifiers(event) || event.buttons !== 0) return;
if (hasModifiers(event) || event.buttons === 2) return;
//
// Get the speech element that was clicked
//
Expand Down Expand Up @@ -464,7 +468,7 @@ export class SpeechExplorer
//
if (
hasModifiers(event) ||
event.buttons !== 0 ||
event.buttons === 2 ||
document.getSelection().type === 'Range'
) {
this.FocusOut(null);
Expand Down
2 changes: 1 addition & 1 deletion ts/a11y/speech/GeneratorPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ export class GeneratorPool<N, T, D> {
adaptor: DOMAdaptor<N, T, D>,
webworker: WorkerHandler<N, T, D>
) {
this.options = options;
if (this._init) return;
this.adaptor = adaptor;
this.options = options;
this.webworker = webworker;
this._init = true;
}
Expand Down
2 changes: 0 additions & 2 deletions ts/output/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ export class SVG<N, T, D> extends CommonOutputJax<
public static OPTIONS: OptionList = {
...CommonOutputJax.OPTIONS,
blacker: 3, // the stroke-width to use for SVG character paths
internalSpeechTitles: true, // insert <title> tags with speech content
titleID: 0, // initial id number to use for aria-labeledby titles
fontCache: 'local', // or 'global' or 'none'
localID: null, // ID to use for local font cache (for single equation processing)
useXlink: true, // true to include xlink namespace for <use> hrefs, false to not
Expand Down
32 changes: 0 additions & 32 deletions ts/output/svg/Wrappers/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,35 +204,6 @@ export const SvgMath = (function <N, T, D>(): SvgMathClass<N, T, D> {
}
}

/**
* Handle adding speech to the top-level node, if any.
*/
protected handleSpeech() {
const adaptor = this.adaptor;
const attributes = this.node.attributes;
const speech = (attributes.get('aria-label') ||
attributes.get('data-semantic-speech')) as string;
if (speech) {
const id = this.getTitleID();
const label = this.svg('title', { id }, [this.text(speech)]);
adaptor.insert(label, adaptor.firstChild(this.dom[0]));
adaptor.setAttribute(this.dom[0], 'aria-labeledby', id);
adaptor.removeAttribute(this.dom[0], 'aria-label');
for (const child of this.childNodes[0].childNodes) {
child.dom.forEach((node) =>
adaptor.setAttribute(node, 'aria-hidden', 'true')
);
}
}
}

/**
* @returns {string} A unique ID to use for aria-labeledby title elements
*/
protected getTitleID(): string {
return 'mjx-svg-title-' + String(this.jax.options.titleID++);
}

/************************************************************/

/**
Expand All @@ -246,9 +217,6 @@ export const SvgMath = (function <N, T, D>(): SvgMathClass<N, T, D> {
adaptor.setAttribute(this.jax.container, 'display', 'true');
this.handleDisplay();
}
if (this.jax.document.options.internalSpeechTitles) {
this.handleSpeech();
}
}

/**
Expand Down
10 changes: 9 additions & 1 deletion ts/ui/menu/MJContextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export class MJContextMenu extends ContextMenu {
*/
public mathItem: MathItem<HTMLElement, Text, Document> = null;

/**
* Records the mathItem's nofocus value when a SelectInfo dialog is opened
*/
public nofocus: boolean = false;

/**
* The document options
*/
Expand Down Expand Up @@ -100,8 +105,11 @@ export class MJContextMenu extends ContextMenu {
*/
public unpost() {
super.unpost();
this.mathItem.outputData.nofocus = false;
if (this.mathItem) {
this.mathItem.outputData.nofocus = this.nofocus;
}
this.mathItem = null;
this.nofocus = false;
}

/*======================================================================*/
Expand Down
94 changes: 63 additions & 31 deletions ts/ui/menu/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ export interface MenuSettings {
enrich: boolean;
inTabOrder: boolean;
assistiveMml: boolean;
roleDescription: string;
// A11y settings
backgroundColor: string;
backgroundOpacity: string;
Expand All @@ -106,6 +105,7 @@ export interface MenuSettings {
viewBraille: boolean;
voicing: boolean;
help: boolean;
roleDescription: string;
}

export type HTMLMATHITEM = MathItem<HTMLElement, Text, Document>;
Expand Down Expand Up @@ -226,6 +226,11 @@ export class Menu {
*/
public menu: MJContextMenu = null;

/**
* The current element being explored
*/
public current: HTMLElement = null;

/**
* A MathML serializer that has options corresponding to the menu settings
*/
Expand Down Expand Up @@ -470,6 +475,13 @@ export class Menu {
''
);

protected postInfo(dialog: Info) {
if (this.menu.mathItem) {
this.menu.nofocus = !!this.menu.mathItem.outputData.nofocus;
}
dialog.post();
}

/*======================================================================*/

/**
Expand Down Expand Up @@ -502,10 +514,12 @@ export class Menu {
this.jax[jax.name] = jax;
this.settings.renderer = jax.name;
this.settings.scale = jax.options.scale;
this.settings.overflow =
jax.options.displayOverflow.substring(0, 1).toUpperCase() +
jax.options.displayOverflow.substring(1).toLowerCase();
this.settings.breakInline = jax.options.linebreaks.inline;
if (jax.options.displayOverflow) {
this.settings.overflow =
jax.options.displayOverflow.substring(0, 1).toUpperCase() +
jax.options.displayOverflow.substring(1).toLowerCase();
}
this.settings.breakInline = jax.options.linebreaks?.inline;
this.defaultSettings = Object.assign(
{},
this.document.options.a11y,
Expand Down Expand Up @@ -556,8 +570,8 @@ export class Menu {
this.a11yVar<boolean>('subtitles'),
this.a11yVar<boolean>('viewBraille'),
this.a11yVar<boolean>('voicing'),
this.variable<string>('roleDescription', (name) =>
this.setRoleDescription(name)
this.a11yVar<string>('roleDescription', () =>
this.setRoleDescription()
),
this.a11yVar<boolean>('help'),
this.a11yVar<string>('locale', (locale) => this.setLocale(locale)),
Expand Down Expand Up @@ -588,19 +602,24 @@ export class Menu {
items: [
this.submenu('Show', 'Show Math As', [
this.command('MathMLcode', 'MathML Code', () =>
this.mathmlCode.post()
this.postInfo(this.mathmlCode)
),
this.command('Original', 'Original Form', () =>
this.originalText.post()
this.postInfo(this.originalText)
),
this.rule(),
this.command('Speech', 'Speech Text', () => this.speechText.post(), {
disabled: true,
}),
this.command(
'Speech',
'Speech Text',
() => this.postInfo(this.speechText),
{
disabled: true,
}
),
this.command(
'Braille',
'Braille Code',
() => this.brailleText.post(),
() => this.postInfo(this.brailleText),
{ disabled: true }
),
this.command('SVG', 'SVG Image', () => this.postSvgImage(), {
Expand All @@ -611,7 +630,7 @@ export class Menu {
this.command(
'Error',
'Error Message',
() => this.errorMessage.post(),
() => this.postInfo(this.errorMessage),
{ disabled: true }
),
]),
Expand Down Expand Up @@ -845,8 +864,8 @@ export class Menu {
),
]),
this.rule(),
this.command('About', 'About MathJax', () => this.about.post()),
this.command('Help', 'MathJax Help', () => this.help.post()),
this.command('About', 'About MathJax', () => this.postInfo(this.about)),
this.command('Help', 'MathJax Help', () => this.postInfo(this.help)),
],
}) as MJContextMenu;
const menu = this.menu;
Expand Down Expand Up @@ -902,8 +921,7 @@ export class Menu {
this.settings.collapsible ||
this.settings.speech ||
this.settings.braille) &&
!MathJax._?.a11y?.['semantic-enrich'] &&
!MathJax._?.a11y?.['speech']
!MathJax._?.a11y?.explorer
) {
this.loadA11y('explorer');
}
Expand Down Expand Up @@ -1025,7 +1043,9 @@ export class Menu {
const options = this.document.outputJax.options;
options.scale = parseFloat(settings.scale);
options.displayOverflow = settings.overflow.toLowerCase();
options.linebreaks.inline = settings.breakInline;
if (options.linebreaks) {
options.linebreaks.inline = settings.breakInline;
}
if (!settings.speechRules) {
const sre = this.document.options.sre;
settings.speechRules = `${sre.domain || 'clearspeak'}-${sre.style || 'default'}`;
Expand Down Expand Up @@ -1187,7 +1207,7 @@ export class Menu {
protected setSpeech(speech: boolean) {
this.enableAccessibilityItems('Speech', speech);
this.document.options.enableSpeech = speech;
if (!speech || MathJax._?.a11y?.['speech']) {
if (!speech || MathJax._?.a11y?.explorer) {
this.rerender(STATE.COMPILED);
} else {
this.loadA11y('explorer');
Expand All @@ -1200,7 +1220,7 @@ export class Menu {
protected setBraille(braille: boolean) {
this.enableAccessibilityItems('Braille', braille);
this.document.options.enableBraille = braille;
if (!braille || MathJax._?.a11y?.['speech']) {
if (!braille || MathJax._?.a11y?.explorer) {
this.rerender(STATE.COMPILED);
} else {
this.loadA11y('explorer');
Expand All @@ -1224,10 +1244,9 @@ export class Menu {
}

/**
* @param {string} name The role description to use for math expressions
* Rerender when the role description changes
*/
protected setRoleDescription(name: string) {
this.setA11y({ roleDescription: name });
protected setRoleDescription() {
this.rerender(STATE.COMPILED);
}

Expand All @@ -1237,7 +1256,7 @@ export class Menu {
protected setEnrichment(enrich: boolean) {
this.document.options.enableEnrichment = enrich;
this.setAccessibilityMenus();
if (!enrich || MathJax._?.a11y?.['semantic-enrich']) {
if (!enrich || MathJax._?.a11y?.explorer) {
this.rerender(STATE.COMPILED);
} else {
this.loadA11y('explorer');
Expand Down Expand Up @@ -1292,6 +1311,11 @@ export class Menu {
'Scale all mathematics (compared to surrounding text) by',
scale + '%'
);
if (this.current) {
const speech = (this.menu.mathItem as any).explorers.speech;
speech.refocus = this.current;
speech.node.focus();
}
if (percent) {
if (percent.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) {
const scale = parseFloat(percent) / 100;
Expand Down Expand Up @@ -1539,7 +1563,7 @@ export class Menu {
.replace(/"currentColor"/g, '"black"');
if (!this.settings.showSRE) {
svg = svg.replace(
/ (?:data-semantic-.*?|role|aria-(?:level|posinset|setsize|owns))=".*?"/g,
/ (?:data-semantic-.*?|data-speech-node|role|aria-(?:level|posinset|setsize|owns))=".*?"/g,
''
);
}
Expand All @@ -1561,7 +1585,7 @@ export class Menu {
* Get the SVG image and post it
*/
public postSvgImage() {
this.svgImage.post();
this.postInfo(this.svgImage);
this.toSVG(this.menu.mathItem).then((svg) => {
const html = this.svgImage.html.querySelector('#svg-image');
html.innerHTML = this.formatSource(svg).replace(/\n/g, '<br>');
Expand All @@ -1586,7 +1610,7 @@ export class Menu {
//
this.menu.post(event);
}
this.zoomBox.post();
this.postInfo(this.zoomBox);
}
}

Expand Down Expand Up @@ -1676,14 +1700,22 @@ export class Menu {
*/
public addMenu(math: HTMLMATHITEM) {
const element = math.typesetRoot;
element.addEventListener(
'mousedown',
() => {
this.menu.mathItem = math;
this.current = (math as any).explorers?.speech?.current;
},
true
);
element.addEventListener(
'contextmenu',
() => {
this.menu.mathItem = math;
const speech = (math as any).explorers?.speech;
if (speech) {
math.outputData.nofocus = !speech.active;
speech.refocus = speech.current;
math.outputData.nofocus = !this.current;
speech.refocus = this.current;
}
},
true
Expand Down Expand Up @@ -1747,7 +1779,7 @@ export class Menu {
*
* @param {keyof MenuSettings} name The setting for which to make a variable
* @param {(value: T) => void} action The action to perform when the variable
* is updated
* is updated
* @returns {object} The JSON for the variable
*
* @template T The type of variable being defined
Expand Down
Loading